Schreiben einer skalaren UDF in Scala

Sie können eine skalare benutzerdefinierte Funktion (UDF) in Scala schreiben. Der Code des Scala-Handlers wird ausgeführt, wenn die UDF aufgerufen wird. Unter diesem Thema wird beschrieben, wie ein Handler in Scala geschrieben und die UDF erstellt wird.

Eine UDF ist eine benutzerdefinierte Funktion, die skalare Ergebnisse zurückgibt, d. h. einen einzelnen Wert und nicht mehrere Zeilen. Allgemeine Informationen zu UDFs finden Sie unter Übersicht zu benutzerdefinierten Funktionen.

Wenn Sie eine UDF erstellen, gehen Sie wie folgt vor:

  1. Schreiben Sie ein Scala-Objekt oder eine Scala-Klasse mit einer Methode, Snowflake aufruft, wenn die UDF aufgerufen wird.

    Weitere Informationen dazu finden Sie unter Implementieren eines Handlers (unter diesem Thema).

  2. Erstellen Sie die UDF in SQL mit dem Befehl CREATE FUNCTION, und geben Sie dabei Ihr Objekt oder Ihre Klasse und Methode als Handler an. Wenn Sie die UDF erstellen, geben Sie Folgendes an:

    • Datentypen der UDF-Eingabeparameter

    • Datentyp des UDF-Rückgabewerts

    • Code, der als Handler ausgeführt wird, wenn die UDF aufgerufen wird

    • Sprache, in der der Handler implementiert ist

    Weitere Informationen zur Syntax von CREATE FUNCTION finden Sie unter Erstellen der UDF mit CREATE FUNCTION.

Sie können eine UDF aufrufen, wie unter Aufrufen einer UDF beschrieben.

Implementieren eines Handlers

Sie implementieren ein Objekt oder eine Klasse mit einer Handler-Methode, die die UDF-Argumentwerten in den Rückgabewert der UDF verarbeitet.

Wenn Sie einen Handler schreiben, müssen Sie Folgendes beachten:

  • Schreiben Sie eine öffentliche Klasse mit einer öffentlichen Methode, die Sie als Handler angeben.

    Dies wird die Methode sein, die Snowflake aufruft, wenn die UDF in SQL aufgerufen wird.

    Sie können mehrere weitere Methoden in demselben Objekt oder derselben Klasse definieren und dann jede als Handler für eine andere UDF verwenden. Dies ist z. B. dann sinnvoll, wenn Sie den kompilierten Handler-Code in einem Stagingbereich aufbewahren und von mehreren Funktionen aus darauf verweisen möchten.

    Weitere Informationen zu Staging-Handlern finden Sie unter Speichern von Handler-Code inline oder in einem Stagingbereich.

  • Schreiben Sie optional einen Null-Argument-Konstruktor für Snowflake, der zur Initialisierung des Handlers aufgerufen wird.

Bemerkung

Achten Sie darauf, dass Ihr Handler die Snowflake-bedingten Einschränkungen in jeder Handler-Methode und den von ihm aufgerufenen Methoden berücksichtigt. Weitere Informationen zu diesen Einschränkungen finden Sie unter Entwerfen von Handlern unter Berücksichtigung der Snowflake-bedingten Einschränkungen.

Beispiel für einen Handler

Der Code im folgenden Beispiel enthält eine Handler-Methode MyHandler.echoVarchar, die eine Zeichenfolge empfängt und zurückgibt. Der von der UDF empfangene Wert (ein VARCHAR-Wert) wird von Snowflake dem Parametertyp der Handler-Methode (eine Zeichenfolge) zugeordnet.

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

Aufrufen der UDF

SELECT echo_varchar('Hello');
Copy

Initialisierung des Handlers

Sie können Ihren Handler optional initialisieren, indem Sie einen Null-Argument-Konstruktor hinzufügen.

Wenn der Konstruktor einen Fehler auslöst, wird dieser Fehler zusammen mit der Ausnahmemeldung als Benutzerfehler ausgelöst.

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

Verarbeiten von Funktionsargumenten

Um Daten zu verarbeiten, die als Argumente an die UDF übergeben werden, implementieren Sie eine öffentliche Methode, die Snowflake aufruft, wenn die UDF im SQL-Code aufgerufen wird. Wenn Sie die UDF mit einem CREATE FUNCTION-Befehl erstellen, verwenden Sie die HANDLER-Klausel, um die Methode als Handler zu spezifizieren.

Wenn Sie eine Handler-Methode deklarieren, müssen Sie Folgendes tun:

  • Deklarieren Sie die Handler-Methode als „public“ (öffentlich).

    Sie können optional einen Null-Argument-Konstruktor einfügen, um den Handler zu initialisieren. Weitere Informationen dazu finden Sie unter Initialisierung des Handlers (unter diesem Thema).

    Wenn Sie beabsichtigen, die Klasse in eine JAR-Datei zu packen und die Datei als Handler in einem Stagingbereich bereitzustellen, können Sie mehrere Handler-Methoden deklarieren und später jede mit der HANDLER-Klausel einer CREATE FUNCTION-Anweisung als Handler spezifizieren. Weitere Informationen zu Staging-Handlern finden Sie unter Speichern von Handler-Code inline oder in einem Stagingbereich.

  • Geben Sie die Parameter- und Rückgabetypen der Handler-Methode an, die den in der UDF-Deklaration angegebenen SQL-Typen entsprechen.

    Weitere Informationen dazu finden Sie unter Zuordnung von Datentypen zwischen SQL und Scala.

  • Optional können zusätzliche Methoden deklariert werden, um die Verarbeitung der Handler-Methode zu unterstützen, z. B. Methoden, die von der Handler-Methode aufgerufen werden.

    Der Code im folgenden Beispiel enthält eine Handler-Methode handleStrings, die eine Nicht-Handler-Methode concatenate aufruft, um die Verarbeitung des als Argument empfangenen Arrays zu unterstützen.

    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

    Im Folgenden wird die Funktion generate_greeting aufgerufen.

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

    Die folgende Abbildung zeigt die Ausgabe des Aufrufs von generate_greeting mit den oben genannten Werten.

    Hello world
    

Überladen von Handler-Methoden

Sie können Handler-Methoden in derselben Klasse oder demselben Objekt überladen, solange die Anzahl der Parameter unterschiedlich ist.

Für Scala-UDFs verwendet Snowflake nur die Anzahl der Methodenargumente, nicht deren Typen, um Handler-Methoden zu unterscheiden. Das Auflösen auf Basis von Datentypen ist unpraktisch, da einige SQL-Datentypen mehr als einem Scala- oder Java-Datentyp und damit potenziell mehr als einer Handler-Methoden-Signatur zugeordnet werden können.

Wenn beispielsweise zwei Scala-Methoden den gleichen Namen und die gleiche Anzahl von Argumenten, aber unterschiedliche Datentypen haben, dann generiert der Aufruf einer UDF mit einer dieser Methoden als Handler einen Fehler, der in etwa wie folgt lautet:

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

Wenn ein Warehouse vorhanden ist, wird der Fehler zum Zeitpunkt der Erstellung der UDF erkannt. Andernfalls tritt der Fehler beim Aufruf der UDF auf.