Java UDF ハンドラーの例

このトピックには、Javaで記述された UDF ハンドラーコードの簡単な例が含まれています。

Javaを使用して UDF ハンドラーを作成する方法の詳細については、 Java UDF ハンドラーの作成 をご参照ください。

このトピックの内容:

単純なインラインJava UDF の作成と呼び出し

次のステートメントは、インラインJava UDF を作成して呼び出します。このコードは、渡された VARCHAR を返します。

この関数は、オプションの CALLED ON NULL INPUT 句を使用して宣言され、入力の値が NULL であっても関数が呼び出されることを示します。(この関数は、この句の有無にかかわらず NULL を返しますが、たとえば空の文字列を返すように、別の方法で NULL を処理するようにコードを変更できます。)

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

UDF を呼び出します。

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

NULL のインラインJava UDF への引き渡し

これは、上記で定義された echo_varchar() UDF を使用します。SQL NULL 値は暗黙的にJava null に変換され、そのJava null が返され、暗黙的に SQL NULL に変換されます。

UDF を呼び出します。

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

配列値の受け渡し

Javaメソッドは、次に挙げる2つの方法のいずれかで SQL 配列を受け取ることができます。

  • Javaの配列機能を使用。

  • Javaの varargs (引数の数が可変)機能を使用。

どちらの場合も、 SQL コードは ARRAY を渡す必要があります。

注釈

必ず、 SQL 型への有効なマッピングを持つJava型を使用してください。詳細については、 SQL-Javaデータ型マッピング をご参照ください。

ARRAY 経由の引き渡し

Javaパラメーターを配列として宣言します。たとえば、次に挙げるメソッドの3番目のパラメーターは文字列配列です。

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

以下は包括的な例です。

テーブルを作成してロードします。

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

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

UDF を呼び出します。

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

Varargs経由の引き渡し

Varargsの使用は、配列の使用と非常によく似ています。

Javaコードでは、Javaのvarargs宣言スタイルを使用します。

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

以下は包括的な例です。この例と前の例(配列の場合)の間における唯一の重要な違いは、メソッドへのパラメーター宣言です。

テーブルを作成してロードします。

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

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

UDF を呼び出します。

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

インライン UDF からの明示的な NULL の戻り

次のコードは、 NULL 値を明示的に返す方法を示しています。Java値 null は SQL NULL に変換されます。

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

UDF を呼び出します。

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

OBJECT のインラインJava UDF への引き渡し

次の例では、 SQL OBJECT データ型と対応するJavaデータ型(Map<文字列,文字列>)を使用し、 OBJECT から値を抽出します。この例は、Java UDF に複数のパラメーターを渡すことができることも示しています。

タイプ OBJECT の列を含むテーブルを作成してロードします。

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

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

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

GEOGRAPHY 値のインラインJava UDF への引き渡し

次の例では、 SQL GEOGRAPHY データ型を使用しています。

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

PACKAGES 句を使用して、 Snowparkパッケージ などのSnowflakeシステムパッケージを指定できます。その場合は、 IMPORTS 句の値としてSnowpark JAR ファイルを含める必要はありません。PACKAGES の詳細については、 CREATE FUNCTION オプションのパラメーター をご参照ください。

データを作成し、そのデータを使用して UDF を呼び出します。

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

出力は、次と類似したものになります。

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

VARIANT 値のインラインJava UDF への引き渡し

SQL VARIANT 型の値をJava UDF に渡すと、Snowflakeはその値を Snowparkパッケージ で提供される バリアント 型に変換できます。 Variant は、Snowparkパッケージバージョン1.4.0以降でサポートされていることに注意してください。

Snowpark Variant 型は、 Variant 型と他のタイプの間で値を変換するためのメソッドを提供します。

Snowpark Variant 型を使用するには、 PACKAGES 句を使用して、 UDF を作成するときにSnowparkパッケージを指定します。その場合は、 IMPORTS 句の値としてSnowpark JAR ファイルを含める必要はありません。PACKAGES の詳細については、 CREATE FUNCTION オプションのパラメーター をご参照ください。

次の例のコードは、 VARIANT 型として保存された JSON データを受け取り、Snowparkライブラリの Variant 型を使用して、 JSON から price 値を取得します。受信した JSON は、 例で使用されるサンプルデータ に表示されている JSON と同様の構造になっています。

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

Java UDF を使用したファイルの読み取り

ハンドラーコードを使用してファイルの内容を読み取ることができます。たとえば、ハンドラーを使用してファイルを読み取り、非構造化データを処理する場合があります。非構造化データの処理とサンプルコードに関する詳細については、 UDF およびプロシージャハンドラーを使用した非構造化データの処理 をご参照ください。

ファイルは、ハンドラーが使用できるSnowflakeのステージである必要があります。

ステージングされたファイルのコンテンツを読み取るために、ハンドラーは次を実行できます。

  • ファイルパスが IMPORTS 句で静的に指定されている ファイルを読み取ります。実行時に、コードは UDF のホームディレクトリからファイルを読み取ります。

    これは、初期化中にファイルにアクセスする場合に役立つ可能性があります。

  • SnowflakeFile クラスまたは InputStream クラスのいずれかのメソッドを呼び出して、動的に指定されたファイルを読み取ります。

    呼び出し元が指定したファイルにアクセスする必要があるときには、この操作を実行する場合があります。詳細については、このトピック内で次をご参照ください。

    SnowflakeFile は、次のテーブルで説明されているように、 InputStream では利用できない機能を提供します。

    クラス

    入力

    メモ

    SnowflakeFile

    URL フォーマット:

    • 関数の呼び出し元がその所有者でもない場合に、ファイルインジェクション攻撃のリスクを軽減するためのスコープ URL を設定しました。

    • ファイル URL または UDF 所有者がアクセス権を持っているファイルの文字列パス。

    ファイルは、名前付き内部ステージまたは外部ステージに配置されている必要があります。

    ファイルサイズなど、追加のファイル属性に簡単にアクセスできます。

    InputStream

    URL フォーマット:

    • 関数の呼び出し元がその所有者でもない場合に、ファイルインジェクション攻撃のリスクを軽減するためのスコープ URL を設定しました。

    ファイルは、内部または外部ステージに配置する必要があります。

前提条件

ファイルをコードで使用できるようにするには、Javaハンドラーコードがステージ上のファイルを読み取る前に、次を実行する必要があります。

  1. ハンドラーが使用できるステージを作成する。

    外部ステージまたは内部ステージを使用できます。内部ステージを使用する場合は、ユーザーまたは名前付きステージにする必要があります。Snowflakeは現在、 UDF 依存関係のテーブルステージの使用をサポートしていません。ステージの作成の詳細については、 CREATE STAGE をご参照ください。内部ステージ型の選択の詳細については、 ローカルファイルに対する内部ステージの選択 をご参照ください。

    ステージから読み取る SQL アクションを実行するロールには、ステージに対する適切な権限を割り当てる必要があることに注意してください。詳細については、 Granting Privileges for User-Defined Functions をご参照ください。

  2. ステージに、コードで読み取られるファイルをコピーする。

    PUT コマンドを使用して、ファイルをローカルドライブからステージにコピーできます。コマンドリファレンスについては、 PUT をご参照ください。PUT を使用したファイルのステージングについては、 ローカルファイルシステムからのデータファイルのステージング をご参照ください。

IMPORTS で静的に指定されたファイルの読み取り

ハンドラーは、ステージングされたパスが CREATE FUNCTION コマンドの IMPORTS 句で指定されているファイルを読み取ることができます。

IMPORTS 句でファイルを指定すると、Snowflakeはそのファイルをステージから UDF の ホームディレクトリ (別称 インポートディレクトリ)にコピーします。UDF は、ホームディレクトリから実際にファイルを読み取ります。

インポートされたファイルは単一のディレクトリにコピーされ、そのディレクトリ内で一意の名前を付ける必要があるため、ファイルが異なるステージまたはステージ内の異なるサブディレクトリで開始された場合でも、 IMPORTS 句の各ファイルに異なる名前を付ける必要があります。

次の例では、ファイルを読み取るJava UDF を作成して呼び出します。

以下のJavaソースコードは、 readFile という名前のJavaメソッドを作成します。この UDF はこのメソッドを使用します。

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

次の SQL コードは UDF を作成します。このコードは、Javaソースコードがコンパイルされ、 UDF がインポートする TestReadRelativeFile.jar という名前の JAR ファイルに配置されていることを前提としています。2番目と3番目のインポートされたファイル my_config_file_1.txtmy_config_file_2.txt は、 UDF が読み取ることができる構成ファイルです。

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

このコードは UDF を呼び出します。

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

圧縮形式または非圧縮形式のどちらでファイルにアクセスするかを選択

ステージ内のファイルは、圧縮形式または非圧縮形式で保存できます。ユーザーは、ファイルをステージにコピーする前に圧縮するか、 PUT コマンドにファイルを圧縮するように指示できます。

Snowflakeが GZIP 形式で圧縮されたファイルをステージから UDF ホームディレクトリにコピーする場合、Snowflakeはコピーをそのまま書き込むか、Snowflakeはファイルを書き込む前にコンテンツを解凍できます。

ステージ内のファイルが圧縮されていて、 UDF ホームディレクトリのコピーも圧縮する場合は、 IMPORTS 句でファイル名を指定するときに、元のファイル名(例:「MyData.txt.gz」)を IMPORTS サブ句で使用します。例:

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

ステージ内のファイルが GZIP 圧縮されているが、 UDF ホームディレクトリのコピーを解凍したい場合は、 IMPORTS 句でファイル名を指定するときに、「.gz」拡張子を省略します。たとえば、ステージに「MyData.txt.gz」が含まれているが、 UDF に非圧縮形式でファイルを読み取らせたい場合は、 IMPORTS 句に「MyData.txt」を指定します。「MyData.txt」という名前の非圧縮ファイルがまだない場合、Snowflakeは「MyData.txt.gz」を検索し、解凍したコピーを UDF ホームディレクトリの「MyData.txt」に自動で書き込みます。UDF は、圧縮されていないファイル「MyData.txt」を開いて読み取ることができます。

スマート解凍は、 UDF ホームディレクトリ内のコピーにのみ適用されます。ステージ内の元のファイルは変更されません。

圧縮ファイルを処理するための次のベストプラクティスに従ってください。

  • 適切なファイル名の規則に従う。ファイルが GZIP 圧縮形式の場合は、ファイル名の末尾に拡張子「.gz」を含める。ファイルが GZIP 圧縮形式でない場合は、ファイル名を「.gz」拡張子で終わらせない。

  • 拡張子「.gz」だけが異なる名前のファイルは作成しない。たとえば、同じステージとディレクトリに「MyData.txt」と「MyData.txt.gz」の両方を作成することや、「MyData.txt」と「MyData.txt.gz」の両方を CREATE FUNCTION コマンドにインポートすることは避けてください。

  • ファイルを2回圧縮しない。たとえば、ファイルを手動で圧縮した後、 AUTO_COMPRESS = FALSE を使用せずにそのファイルを PUT すると、ファイルはもう一度圧縮されます。スマート解凍では1回だけ解凍されるため、データ(または JAR)ファイルが UDF ホームディレクトリに保存されるときには、引き続き圧縮されたままになります。

  • 将来、Snowflakeは、スマート解凍を GZIP 以外の圧縮アルゴリズムに拡張する可能性があります。将来の互換性の問題を防ぐために、これらのベストプラクティスを任意のタイプの圧縮を使用するファイルに適用してください。

注釈

JAR ファイルは、圧縮または非圧縮形式でステージに保存することもできます。Snowflakeは、圧縮されたすべての JAR ファイルを自動的に解凍してから、Java UDF で使用できるようにします。

SnowflakeFile を使用した動的に指定されたファイルの読み取り

SnowflakeFile クラスのメソッドを使用すると、Javaハンドラーコードを使用してステージからファイルを読み取ることができます。 SnowflakeFile クラスは、SnowflakeのJava UDF ハンドラーが使用できるクラスパスに含まれています。

注釈

ファイルインジェクション攻撃に対するコードの回復性を高めるには、ファイルの場所を UDF に渡す場合、特に関数の呼び出し元がその所有者でもない場合に、スコープ URL を常に使用します。組み込み関数 BUILD_SCOPED_FILE_URL を使用して、スコープ URL を SQL に作成できます。BUILD_SCOPED_FILE_URL の機能の詳細については、 非構造化データのロードの概要 をご参照ください。

UDF コードをローカルで開発するには、 SnowflakeFile を含んでいるSnowpark JAR をコードのクラスパスに追加します。 snowpark.jar については、 Snowpark Javaの開発環境の設定 をご参照ください。Snowparkクライアントアプリケーションは、このクラスを使用できないことに注意してください。

SnowflakeFile を使用する場合は、 SQL で CREATE FUNCTION ステートメントを使用する場合のように、 UDF の作成時に、ステージングされたファイル、または SnowflakeFile を含んでいる JAR のいずれかを IMPORTS 句で指定する必要はありません。

次の例のコードは、 SnowflakeFile を使用して、指定されたステージの場所からファイルを読み取ります。 getInputStream メソッドの InputStream を使用して、ファイルの内容を 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

UDF を呼び出してスコープ URL でファイルの場所を渡し、ファイルインジェクション攻撃の可能性を減らします。

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

注釈

UDF 所有者は、場所がスコープ付き URLs ではないファイルへのアクセス権を持っている必要があります。新しい requireScopedUrl パラメーターに boolean 値を指定して SnowflakeFile.newInstance メソッドを呼び出すハンドラーコードにより、これらのステージングされたファイルを読み取ることができます。

次の例では、スコープ付き URL が不要であることを指定し、 SnowflakeFile.newInstance を使用します。

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

InputStream を使用した動的に指定されたファイルの読み取り

ハンドラー関数の引数を InputStream 変数にすると、ファイルの内容を直接 java.io.InputStream に読み込むことができます。これは、関数の呼び出し元が引数としてファイルパスを渡す場合に役立ちます。

注釈

ファイルインジェクション攻撃に対するコードの回復性を高めるには、ファイルの場所を UDF に渡す場合、特に関数の呼び出し元がその所有者でもない場合に、スコープ URL を常に使用します。組み込み関数 BUILD_SCOPED_FILE_URL を使用して、スコープ URL を SQL に作成できます。BUILD_SCOPED_FILE_URL の機能の詳細については、 非構造化データのロードの概要 をご参照ください。

次の例のコードには、 InputStream を受け取り、 int を返すハンドラー関数 sumTotalSales があります。実行時に、Snowflakeは file 変数のパスにあるファイルの内容を 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

UDF を呼び出してスコープ URL でファイルの場所を渡し、ファイルインジェクション攻撃の可能性を減らします。

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

ステージングされた簡単なJava UDF の作成と呼び出し

次のステートメントは、単純なJava UDF を作成します。このサンプルは通常、 ファイルの整理 で説明されているファイルとディレクトリの構造に従います。

Javaハンドラーコードを作成しコンパイルする

  1. プロジェクトのルートディレクトリ(ここでは my_udf)に、ソース.javaファイルを保持する src サブディレクトリと、生成された.classファイルを保持する classes サブディレクトリを作成します。

    次のようなディレクトリ階層が必要です。

    my_udf/
    |-- classes/
    |-- src/
    
    Copy
  2. src ディレクトリに、クラスが mypackage パッケージにある.javaファイルを保持する、 mypackage というディレクトリを作成します。

  3. mypackage ディレクトリに、ソースコードを含む MyUDFHandler.java ファイルを作成します。

    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. プロジェクトのルートディレクトリ(ここでは my_udf)から、 javac コマンドを使用してソースコードをコンパイルします。

    次の例の javac コマンドは、 MyUDFHandler.java をコンパイルして、 classes ディレクトリに MyUDFHandler.class ファイルを生成します。

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

    この例には、次の引数が含まれています。

    • -d classes --- 生成されたクラスファイルを書き込むディレクトリ。

    • src/mypackage/MyUDFHandler.java --- .javaファイルへの source_directory/package_directory/Java_file_name 形式のパス。

コンパイルされたコードを JAR ファイルにパッケージ化する

  1. オプションで、プロジェクトのルートディレクトリに、次の属性を含む my_udf.manifest という名前のマニフェストファイルを作成します。

    Manifest-Version: 1.0
    Main-Class: mypackage.MyUDFHandler
    
    Copy
  2. プロジェクトのルートディレクトリから、 jar コマンドを実行して、.classファイルとマニフェストを含む JAR ファイルを作成します。

    次の例の jar コマンドは、 mypackage パッケージフォルダーに生成された MyUDFHandler.class ファイルを my_udf.jar という.jarファイルに配置します。 -C ./classes フラグは、.classファイルの場所を指定します。

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

    この例には、次の引数が含まれています。

    • cmf --- コマンド引数: c は JAR ファイルを作成し、 m は指定された.manifestファイルを使用し、 f は JAR ファイルに指定された名前を付けます。

    • my_udf.manifest --- マニフェストファイル。

    • my_udf.jar --- 作成する JAR ファイルの名前。

    • -C ./classes --- 生成された.classファイルを含むディレクトリ。

    • mypackage/MyUDFHandler.class --- JAR に含める.classファイルのパッケージと名前。

コンパイルされたハンドラーを使用して JAR ファイルをステージにアップロードする

  1. Snowflakeで、 UDF ハンドラーを含む JAR ファイルを保存するために、 jar_stage というステージを作成します。

    ステージの作成の詳細については、 CREATE STAGE をご参照ください。

  2. PUT コマンドを使用して、 JAR ファイルをローカルファイルシステムからステージにコピーします。

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

PUT コマンドをスクリプトファイルに保存してから、 SnowSQL を介してそのファイルを実行できます。

snowsql コマンドは次のようになります。

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

この例では、ユーザーのパスワードが SNOWSQL_PWD 環境変数で指定されていることを前提としています。

コンパイルされたコードをハンドラーとして UDF を作成する

UDF を作成します。

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

UDF を呼び出します。

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