Schreiben von gespeicherten Prozeduren in Snowpark (Java)

Dieses Feature ermöglicht das Schreiben von gespeicherten Prozeduren in Java. Sie können die Snowpark-Bibliothek innerhalb Ihrer gespeicherten Prozedur verwenden, um Abfragen, Aktualisierungen und andere Arbeiten an Tabellen in Snowflake auszuführen.

Unter diesem Thema wird erklärt, wie Sie eine gespeicherte Prozedur in Java schreiben.

Unter diesem Thema:

Einführung

Mit gespeicherten Snowpark-Prozeduren können Sie Ihre Datenpipeline in Snowflake erstellen und ausführen, wobei ein Snowflake-Warehouse als Compute-Framework dient. Für den Code Ihrer Datenpipeline verwenden Sie die Snowpark-API für Java zum Schreiben der gespeicherten Prozeduren. Um die Ausführung dieser gespeicherten Prozeduren zu planen, verwenden Sie Aufgaben (Tasks).

Bemerkung

Um eine anonyme Prozedur sowohl zu erstellen als auch aufzurufen, verwenden Sie CALL (mit anonymen Prozeduren). Das Erstellen und Aufrufen einer anonymen Prozedur erfordert keine Rolle mit CREATE PROCEDURE-Schemaberechtigungen.

Einschränkungen

In dieser Vorschau bestehen für gespeicherte Snowpark-Prozeduren die folgenden Einschränkungen:

  • Parallelität wird nicht unterstützt. So können Sie beispielsweise über Ihren Code keine Abfragen von mehreren Threads übermitteln. Code, der mehrere Abfragen gleichzeitig ausführt, führt zu einem Fehler.

  • Wenn Sie Ihre gespeicherte Prozedur von einer Aufgabe aus ausführen, müssen Sie beim Erstellen der Aufgabe ein Warehouse angeben. (Sie können für die Ausführung der Aufgabe keine von Snowflake verwalteten Computeressourcen verwenden).

  • Das Lesen und Schreiben von Dateien aus in Java geschriebenem Handler-Code von gespeicherten Prozeduren wird noch nicht vollständig unterstützt. Das Lesen und Schreiben von Dateien kann zu instabilem Verhalten führen. Dazu zählt das Empfangen eines InputStream als Argument und die Verwendung von Methoden, die in der Klasse FileOperation verfügbar sind (auf die Sie normalerweise über die Methode Session.file zugreifen), einschließlich put und get.

  • Beachten Sie die folgenden Einschränkungen für die Verwendung von Snowpark-APIs in Ihrer gespeicherten Prozedur.

    • Wenn Sie APIs verwenden, die PUT- und GET-Befehle ausführen (einschließlich Session.sql("PUT ...") und Session.sql("GET ...")), dürfen Sie nur in das Verzeichnis „/tmp“ des speichergestützten Dateisystems schreiben, das für die Abfrage vorgesehen ist, das die Prozedur aufruft.

    • Verwenden Sie keine APIs für asynchrone Aktionen.

    • Verwenden Sie keine APIs, die neue Sitzungen erstellen (z. B. Session.builder().configs(...).create()).

    • Die Verwendung von session.jdbcConnection (und der zurückgegebenen Verbindung) wird nicht unterstützt, da dies zu unsicherem Verhalten führen kann.

Voraussetzungen

Sie müssen Version 1.3.0 oder eine neuere Version der Snowpark-Bibliothek verwenden.

Wenn Sie eine vorkompilierte gespeicherte Prozedur schreiben, müssen Sie Ihre Klassen für die Ausführung in Java, Version 11.x kompilieren.

Einrichten Ihrer Entwicklungsumgebung für Snowpark

Als Nächstes richten Sie Ihre Entwicklungsumgebung für die Verwendung der Snowpark-Bibliothek ein. Siehe Einrichten Ihrer Entwicklungsumgebung für Snowpark Java.

Auswählen zwischen inline oder vorkompilierter gespeicherter Prozedur

Wie bei Java-UDFs können Sie entweder eine inline gespeicherte Prozedur oder eine vorkompilierte gespeicherte Prozedur erstellen.

  • In einer inline gespeicherten Prozedur schreiben Sie den Java-Code in die AS-Klausel der CREATE PROCEDURE-Anweisung. Beispiel:

    CREATE OR REPLACE PROCEDURE MYPROC(fromTable STRING, toTable STRING, count INT)
      RETURNS STRING
      LANGUAGE JAVA
      RUNTIME_VERSION = '11'
      PACKAGES = ('com.snowflake:snowpark:latest')
      HANDLER = 'MyJavaClass.run'
      AS
      $$
        import com.snowflake.snowpark_java.*;
    
        public class MyJavaClass {
          public String run(Session session, String fromTable, String toTable, int count) {
            session.table(fromTable).limit(count).write().saveAsTable(toTable);
            return "SUCCESS";
          }
        }
      $$;
    

    Bemerkung

    Um die Ausführung bei wiederholten Aufrufen zu beschleunigen, können Sie TARGET_PATH auf den Speicherort einer JAR-Datei setzen, an dem Snowflake die kompilierten Klassen speichern soll. Siehe Erstellen der gespeicherten Prozedur.

  • In einer vorkompilierten gespeicherten Prozedur schreiben Sie Ihren Java-Code in eine .java-Quelldatei.

    Beispiel:

    import com.snowflake.snowpark_java.*;
    
    public class MyJavaClass {
      public String run(Session session, String fromTable, String toTable, int count) {
        session.table(fromTable).limit(count).write().saveAsTable(toTable);
        return "SUCCESS";
      }
    }
    

    Anschließend kompilieren Sie Ihren Code, packen die Klassen in eine JAR-Datei, laden die JAR-Datei in einen Stagingbereich und führen den CREATE PROCEDURE-Befehl aus, der auf die JAR-Datei im Stagingbereich verweist. Beispiel:

    CREATE OR REPLACE PROCEDURE MYPROC(value INT, fromTable STRING, toTable STRING, count INT)
      RETURNS INT
      LANGUAGE JAVA
      RUNTIME_VERSION = '11'
      PACKAGES = ('com.snowflake:snowpark:latest')
      IMPORTS = ('@mystage/MyCompiledJavaCode.jar')
      HANDLER = 'MyJavaClass.run';
    

Schreiben des Java-Codes für die gespeicherte Prozedur

Für den Code Ihrer gespeicherten Prozedur müssen Sie eine Java-Methode schreiben. Die folgenden Abschnitte enthalten Richtlinien für die Erstellung Ihres Codes:

Planen des Schreibens Ihrer gespeicherten Prozedur

Der Java-Code für Ihre gespeicherte Prozedur unterliegt denselben Einschränkungen wie der Code einer Java-UDF. Bei der Planung Ihrer gespeicherten Prozeduren sollten Sie diese Einschränkungen berücksichtigen.

Belegung des Arbeitsspeichers begrenzen

Wie bei Java-UDFs belegt Snowflake Methoden mit einer Begrenzung hinsichtlich der benötigten Menge an Arbeitsspeicher.

In Ihrer Methode sollten Sie darauf achten, nicht zu viel Arbeitsspeicher zu verbrauchen.

Thread-sicheren Code schreiben

Ähnlich wie bei Java-UDFs müssen Sie sicherstellen, dass Ihre Methode thread-sicher ist.

Erläuterungen zu Sicherheitseinschränkungen

Ihre Methode wird innerhalb einer eingeschränkten Engine ausgeführt. Daher müssen Sie die gleichen Regeln befolgen, wie sie für Java-UDFs dokumentiert sind.

Entscheiden Sie, ob Sie die Eigentümerrechte oder die Aufruferrechte nutzen möchten.

Außerdem müssen Sie bei der Planung Ihrer gespeicherten Prozedur berücksichtigen, ob Sie die gespeicherte Prozedur mit Aufruferrechten oder Eigentümerrechten ausführen möchten.

Timeout-Verhalten von gespeicherten Prozeduren berücksichtigen

Die Ausführung einer gespeicherten Prozedur wird zeitlich begrenzt, es sei denn, der Timer wird durch die Aktivität des Codes zurückgesetzt. Insbesondere wird der Timeout-Timer durch die Interaktionen des Codes mit Daten zurückgesetzt, einschließlich Dateioperationen, Abfragen und Iterationen durch ein Resultset.

Schreiben der Klasse

Die Methode, die Sie definieren, muss Teil einer Klasse sein.

Beim Schreiben der Klasse ist Folgendes zu beachten:

  • Die Klasse und die Methode dürfen nicht geschützt oder privat sein.

  • Wenn die Methode nicht statisch ist und Sie einen Konstruktor definieren möchten, definieren Sie einen Null-Argument-Konstruktor für die Klasse. Snowflake ruft diesen Null-Argument-Konstruktor zur Initialisierungszeit auf, um eine Instanz Ihrer Klasse zu erstellen.

  • Sie können in derselben Klasse verschiedene Methoden für verschiedene gespeicherte Prozeduren definieren.

Schreiben der Methode

Beachten Sie beim Schreiben der Methode für die gespeicherte Prozedur Folgendes:

  • Geben Sie das Snowpark-Session-Objekt als erstes Argument Ihrer Methode an.

    Wenn Sie Ihre gespeicherte Prozedur aufrufen, erstellt Snowflake automatisch ein Session-Objekt und übergibt dieses an Ihre gespeicherte Prozedur. (Sie können das Session-Objekt nicht selbst erstellen.)

  • Für die restlichen Argumente und den Rückgabewert werden die Java-Typen verwendet, die den Snowflake-Datentypen entsprechen.

  • Ihre Methode muss einen Wert zurückgeben. Für gespeicherte Prozeduren in Java ist ein Rückgabewert erforderlich.

Zugriff auf Daten in Snowflake über Ihre gespeicherte Prozedur

Um auf Daten in Snowflake zuzugreifen, verwenden Sie die Snowpark-Bibliotheks-APIs.

Bei der Verarbeitung eines Aufrufs Ihrer gespeicherten Java-Prozedur erstellt Snowflake ein Snowpark-Session-Objekt und übergibt das Objekt an die Methode für Ihre gespeicherte Prozedur.

Wie bei gespeicherten Prozeduren in anderen Sprachen wird der Kontext für die Sitzung (z. B. die Berechtigungen, die aktuelle Datenbank, das aktuelle Schema usw.) dadurch bestimmt, ob die gespeicherte Prozedur mit Aufruferrechten oder mit Eigentümerrechten ausgeführt wird. Weitere Details dazu finden Sie unter Zugriff auf und Einstellung des Sitzungsstatus.

Sie können dieses Session-Objekt verwenden, um APIs in der Snowpark-Bibliothek aufzurufen. Sie können zum Beispiel einen DataFrame für eine Tabelle erstellen oder eine SQL-Anweisung ausführen.

Weitere Informationen dazu finden Sie im Snowpark-Entwicklerhandbuch für Java.

Bemerkung

Weitere Informationen zu Einschränkungen, einschließlich Einschränkungen beim Zugriff auf Daten, finden Sie unter Einschränkungen.

Es folgt ein Beispiel für eine Java-Methode, die eine bestimmte Anzahl von Zeilen aus einer Tabelle in eine andere Tabelle kopiert. Der Methode werden die folgenden Argumente übergeben:

  • Ein Snowpark-Session-Objekt

  • Der Name der Tabelle, aus der die Zeilen kopiert werden sollen

  • Der Name der Tabelle, in der die Zeilen gespeichert werden sollen

  • Die Anzahl der zu kopierenden Zeilen

Die Methode in diesem Beispiel gibt eine Zeichenfolge zurück.

import com.snowflake.snowpark_java.*;

public class MyClass
{
  public String myMethod(Session session, String fromTable, String toTable, int count)
  {
    session.table(fromTable).limit(count).write().saveAsTable(toTable);
    return "Success";
  }
}

Zugriff auf andere Klassen und Ressourcendateien

Wenn Ihr Code von Klassen abhängt, die außerhalb der gespeicherten Prozedur definiert sind (z. B. Klassen in einer separaten JAR-Datei) oder von Ressourcendateien, müssen Sie diese Dateien in einen Stagingbereich hochladen, damit die Dateien bei Ausführung der gespeicherten Prozedur verfügbar sind.

Bei der späteren Ausführung der CREATE PROCEDURE-Anweisung verwenden Sie die IMPORTS-Klausel, um auf diese Dateien zu verweisen.

Vorbereiten einer vorkompilierten gespeicherten Prozedur

Wenn Sie eine vorkompilierte gespeicherte Prozedur erstellen möchten (anstelle einer inline gespeicherten Prozedur), müssen Sie Ihre Klassen in einer JAR-Datei kompilieren und packen und die JAR-Datei dann in einen Stagingbereich hochladen.

Kompilieren und Verpacken Ihres Java-Codes

Um das Einrichten Ihrer gespeicherten Prozedur zu vereinfachen, erstellen Sie eine JAR-Datei, die alle für Ihre gespeicherte Prozedur benötigten Abhängigkeiten enthält. Später müssen Sie die JAR-Datei in einen Stagingbereich hochladen und in Ihrer CREATE PROCEDURE-Anweisung auf die JAR-Datei verweisen. Dieser Vorgang ist einfacher, wenn Sie weniger JAR-Dateien hochladen und auf sie verweisen müssen.

Im nächsten Abschnitte finden Sie einige Tipps zum Erstellung einer JAR-Datei, die alle Abhängigkeiten enthält:

Verwenden von Maven zum Erstellen einer JAR-Datei mit Abhängigkeiten

Wenn Sie Maven zum Erstellen und Packen Ihres Codes verwenden, können Sie das Maven Assembly Plugin verwenden, um eine JAR-Datei zu erstellen, die alle Abhängigkeiten enthält.

  1. Erstellen Sie in dem Verzeichnis Ihres Projekts (z. B. hello-snowpark/) ein Unterverzeichnis mit dem Namen assembly/.

  2. Erstellen Sie in diesem Verzeichnis eine Assembly-Deskriptordatei, die angibt, dass Sie Abhängigkeiten zu Ihrer JAR-Datei hinzufügen möchten.

    Ein Beispiel dazu finden Sie unter jar-with-dependencies.

  3. Fügen Sie im Assembly-Deskriptor ein <dependencySet>-Element hinzu, das die Snowpark-Bibliothek von Ihrer JAR-Datei ausschließt.

    Beispiel:

    <assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
      <id>jar-with-dependencies</id>
      <formats>
         <format>jar</format>
      </formats>
      <includeBaseDirectory>false</includeBaseDirectory>
      <dependencySets>
        <dependencySet>
          <outputDirectory>/</outputDirectory>
          <useProjectArtifact>false</useProjectArtifact>
          <unpack>true</unpack>
          <scope>provided</scope>
          <excludes>
            <exclude>com.snowflake:snowpark</exclude>
          </excludes>
        </dependencySet>
      </dependencySets>
    </assembly>
    

    Weitere Informationen zu Elementen in einem Assembly-Deskriptor finden Sie unter Assembly Deskriptor-Format.

  4. Fügen Sie in Ihrer pom.xml-Datei unter <project> » <build> » <plugins> ein <plugin>-Element für das Maven Assembly Plugin hinzu.

    Fügen Sie außerdem unter <configuration> » <descriptors> einen <descriptor> hinzu, der auf die in den vorherigen Schritten erstellte Assembly-Deskriptordatei verweist.

    Beispiel:

    <project>
      [...]
      <build>
        [...]
        <plugins>
          <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.3.0</version>
            <configuration>
              <descriptors>
                <descriptor>src/assembly/jar-with-dependencies.xml</descriptor>
              </descriptors>
            </configuration>
            [...]
          </plugin>
          [...]
        </plugins>
        [...]
      </build>
      [...]
    </project>
    

Verwenden anderer Tools zum Erstellen einer JAR-Datei mit Abhängigkeiten

Wenn Sie Maven nicht verwenden, finden Sie in der Dokumentation Ihres Erstellungstools eine Anleitung zum Erstellen einer JAR-Datei mit allen Abhängigkeiten.

Wenn Sie beispielsweise ein IntelliJ IDEA-Projekt verwenden, finden Sie die erforderlichen Informationen in der Anleitung zum Einrichten einer Artefakt-Konfiguration.

Hochladen von Dateien in einen Stagingbereich

Als Nächstes laden Sie die für Ihre gespeicherte Prozedur erforderlichen Dateien in einen Stagingbereich hoch:

  1. Wählen Sie den Stagingbereich für Ihre Dateien aus.

    Sie können einen externen oder einen benannten internen Stagingbereich verwenden. Wenn Sie die Dateien mit dem Befehl PUT hochladen möchten, verwenden Sie einen benannten internen Stagingbereich. (Mit dem PUT-Befehl können keine Dateien in externe Stagingbereiche hochgeladen werden.)

    Sie können einen vorhandenen Stagingbereich verwenden oder einen neuen Stagingbereich erstellen, indem Sie CREATE STAGE ausführen. Der folgende Befehl erstellt beispielsweise einen neuen internen Stagingbereich namens mystage:

    CREATE STAGE mystage;
    

    Bemerkung

    Der Eigentümer der gespeicherten Prozedur muss die READ-Berechtigung für den Stagingbereich haben.

  2. Verwenden Sie den Befehl PUT, um die folgenden Dateien in diesen Stagingbereich hochzuladen:

    • Die JAR-Datei, die Ihren kompilierten Java-Code enthält.

    • Alle anderen Dateien, von denen Ihre gespeicherte Prozedur abhängt.

    Beispiel:

    PUT file:///Users/MyUserName/myjar.jar
            @mystage
            AUTO_COMPRESS = FALSE
            OVERWRITE = TRUE
            ;
    

    Bemerkung

    Wenn Sie AUTO_COMPRESS = FALSE weglassen, komprimiert der PUT-Befehl die Datei automatisch. Der Name der komprimierten Datei im Stagingbereich wird myjar.jar.gz sein. Wenn Sie später den Befehl CREATE PROCEDURE ausführen, müssen Sie den Dateinamen in der IMPORTS-Klausel mit dieser .gz-Erweiterung angeben.

Beachten Sie, dass Sie die gespeicherte Prozedur nicht mehr aufrufen können, wenn Sie die JAR-Datei, die Ihren kompilierten Java-Code enthält, löschen oder umbenennen.

Wenn Sie Ihre JAR-Datei aktualisieren müssen, gehen Sie wie folgt vor:

  • Aktualisieren Sie die Datei, wenn keine Aufrufe an die gespeicherte Prozedur erfolgen können.

  • Fügen Sie die Klausel OVERWRITE=TRUE zum Befehl PUT hinzu.

Erstellen der gespeicherten Prozedur

Um eine gespeicherte Prozedur zu erstellen, führen Sie die Anweisung CREATE PROCEDURE aus:

Bemerkung

Um eine anonyme Prozedur sowohl zu erstellen als auch aufzurufen, verwenden Sie CALL (mit anonymen Prozeduren). Das Erstellen und Aufrufen einer anonymen Prozedur erfordert keine Rolle mit CREATE PROCEDURE-Schemaberechtigungen.

Legen Sie die in der nachfolgenden Tabelle aufgeführten CREATE PROCEDURE-Parameter fest.

Parameter

Beschreibung

[ arg_name arg_data_type [, ... ] ]

  • Lassen Sie das Argument für das Snowpark-Session-Objekt weg. Wie bereits erwähnt, handelt es sich hierbei nicht um einen formalen Parameter, den Sie in CREATE PROCEDURE oder CALL angeben. Wenn Sie Ihre gespeicherte Prozedur aufrufen, erstellt Snowflake ein Session-Objekt und übergibt dieses an Ihre gespeicherte Prozedur.

  • Verwenden Sie für die Datentypen der übrigen Argumente die Snowflake-Datentypen, die den Java-Typen der Argumente in Ihrer Methode entsprechen.

RETURNS result_data_type

Geben Sie für RETURNS den Snowflake-Datentyp Ihres Rückgabewerts an.

LANGUAGE JAVA

Die müssen Sie angeben, um anzuzeigen, dass Ihr gespeicherter Prozedurcode in Java geschrieben ist.

RUNTIME_VERSION = '11'

Dies müssen Sie angeben, um anzuzeigen, dass Ihre gespeicherte Prozedur Java 11 verwendet.

PACKAGES = ( 'package_name' )

Nehmen Sie in diese Liste das Paket für die Version der Snowpark-Bibliothek auf, die Sie verwenden möchten.

Geben Sie den vollqualifizierten Paketnamen der Snowpark-Bibliothek in folgendem Format an:

com.snowflake:snowpark:<version>

Beispiel:

  • Für die neueste Version von Snowpark verwenden Sie:

    PACKAGES = ('com.snowflake:snowpark:latest')
    
  • Für die 1.6.2-Version von Snowpark verwenden Sie:

    PACKAGES = ('com.snowflake:snowpark:1.6.2')

Bemerkung

Wenn Sie eine neue gespeicherte Prozedur erstellen, geben Sie die Version 1.3.0 oder höher an.

Für die Liste der unterstützten Pakete und Versionen fragen Sie die Ansicht INFORMATION_SCHEMA.PACKAGES nach Zeilen mit LANGUAGE = 'java' ab. Beispiel:

select * from information_schema.packages where language = 'java';

IMPORTS = ( 'file' [, 'file' ... ] )

Wenn Ihre gespeicherte Prozedur von JAR-Dateien abhängt, die Sie in einen Stagingbereich hochgeladen haben, nehmen Sie diese Dateien in die Liste auf.

Wenn Sie eine inline gespeicherte Prozedur schreiben, können Sie diese Klausel weglassen, es sei denn, Ihr Code hängt von Klassen ab, die außerhalb der gespeicherten Prozedur oder der Ressourcendateien definiert sind.

Wenn Sie eine vorkompilierte gespeicherte Prozedur schreiben, müssen Sie auch die JAR-Datei einschließen, die die Definition der gespeicherten Prozedur enthält.

HANDLER = 'method_name'

Setzen Sie dies auf den vollqualifizierten Namen Ihrer Java-Methode.

TARGET_PATH = 'jar_file'

Wenn Sie eine inline gespeicherte Prozedur schreiben und nicht möchten, dass Snowflake Ihren Code bei jedem Aufruf neu kompiliert, können Sie hier die JAR-Datei angeben, die Snowflake für Ihren kompilierten Code erstellen soll.

Dies muss ein Pfad zu einem Stagingbereich sein, für den Sie WRITE-Berechtigungen haben.

EXECUTE AS CALLER

Wenn Sie geplant haben, die gespeicherte Prozedur so einzurichten, dass sie Aufruferrechte verwendet, fügen Sie diesen Parameter hinzu.

Wenn Sie dagegen Eigentümerrechte verwenden möchten, lassen Sie diesen Parameter einfach weg.

In den folgenden Beispielen werden gespeicherte Prozeduren in Java erstellt.

Beispiel 1: Inline gespeicherte Prozedur

CREATE OR REPLACE PROCEDURE myProc(fromTable STRING, toTable STRING, count INT)
RETURNS STRING
LANGUAGE JAVA
RUNTIME_VERSION = '11'
PACKAGES = ('com.snowflake:snowpark:latest')
HANDLER = 'MyClass.myMethod'
AS
$$
  import com.snowflake.snowpark_java.*;

  public class MyClass
  {
    public String myMethod(Session session, String fromTable, String toTable, int count)
    {
      session.table(fromTable).limit(count).write().saveAsTable(toTable);
      return "Success";
    }
  }
$$;

Beispiel 2: Vorkompilierte gespeicherte Prozedur, die kompilierten Code aus der im internen Stagingbereich mystage gespeicherten JAR-Datei myjar.jar verwendet

CREATE OR REPLACE PROCEDURE myProc(fromTable STRING, toTable STRING, count INT)
RETURNS STRING
LANGUAGE JAVA
RUNTIME_VERSION = '11'
PACKAGES = ('com.snowflake:snowpark:latest')
IMPORTS = ('@mystage/myjar.jar')
HANDLER = 'MyClass.myMethod';

Aufrufen Ihrer gespeicherten Prozedur

Damit ein Benutzer eine gespeicherte Prozedur aufrufen kann, muss die Rolle des Benutzers die USAGE-Berechtigung für diese gespeicherte Prozedur haben.

Sobald Sie die Berechtigung zum Aufrufen der gespeicherten Prozedur haben, können Sie mit der CALL-Anweisung die gespeicherte Prozedur aufrufen. Beispiel:

CALL myProc('table_a', 'table_b', 5);
Zurück zum Anfang