Java UDF ์ฒ˜๋ฆฌ๊ธฐ์˜ ์˜ˆยถ

์ด ํ•ญ๋ชฉ์—๋Š” Java๋กœ ์ž‘์„ฑ๋œ UDF ์ฒ˜๋ฆฌ๊ธฐ ์ฝ”๋“œ์˜ ๊ฐ„๋‹จํ•œ ์˜ˆ๊ฐ€ ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค.

Java๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ UDF ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ๋งŒ๋“œ๋Š” ์ž์„ธํ•œ ๋ฐฉ๋ฒ•์€ Java UDF ์ฒ˜๋ฆฌ๊ธฐ ์ƒ์„ฑํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ด ํ•ญ๋ชฉ์˜ ๋‚ด์šฉ:

๊ฐ„๋‹จํ•œ ์ธ๋ผ์ธ Java UDF ๋งŒ๋“ค๊ธฐ ๋ฐ ํ˜ธ์ถœํ•˜๊ธฐยถ

๋‹ค์Œ ๋ฌธ์€ ์ธ๋ผ์ธ Java UDF๋ฅผ ๋งŒ๋“ค๊ณ  ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ด ์ฝ”๋“œ๋Š” ์ด์— ์ „๋‹ฌ๋œ VARCHAR ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ด ํ•จ์ˆ˜๋Š” ์ž…๋ ฅ ๊ฐ’์ด NULL์ธ ๊ฒฝ์šฐ์—๋„ ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋จ์„ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•ด ์„ ํƒ์  CALLED ON NULL INPUT ์ ˆ๋กœ ์„ ์–ธ๋ฉ๋‹ˆ๋‹ค. (์ด ํ•จ์ˆ˜๋Š” ์ด ์ ˆ์„ ํฌํ•จํ•˜๊ฑฐ๋‚˜ ํฌํ•จํ•˜์ง€ ์•Š๊ณ  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

์ธ๋ผ์ธ Java UDF์— NULL ์ „๋‹ฌํ•˜๊ธฐยถ

์ด๋Š” ์œ„์—์„œ ์ •์˜ํ•œ echo_varchar() UDF๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. SQL NULL ๊ฐ’์€ ์•”์‹œ์ ์œผ๋กœ Java null ๋กœ ๋ณ€ํ™˜๋˜๊ณ  ํ•ด๋‹น Java null ์ด ๋ฐ˜ํ™˜๋˜์–ด ์•”์‹œ์ ์œผ๋กœ SQL NULL ๋กœ ๋‹ค์‹œ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค.

UDF๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

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

๋ฐฐ์—ด ๊ฐ’ ์ „๋‹ฌํ•˜๊ธฐยถ

Java ๋ฉ”์„œ๋“œ๋Š” ๋‹ค์Œ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋กœ SQL ๋ฐฐ์—ด์„ ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Java์˜ ๋ฐฐ์—ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • Java์˜ varargs (๊ฐ€๋ณ€ ์ธ์ž ์ˆ˜) ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋‘ ๊ฒฝ์šฐ ๋ชจ๋‘ SQL ์ฝ”๋“œ๋Š” ARRAY ๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ 

SQL ์œ ํ˜•์— ์œ ํšจํ•œ ๋งคํ•‘๊ณผ ํ•จ๊ป˜ Java ์œ ํ˜•์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ SQL-Java ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋งคํ•‘ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

ARRAY๋ฅผ ํ†ตํ•ด ์ „๋‹ฌยถ

Java ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ๋ฐฐ์—ด๋กœ ์„ ์–ธํ•˜์‹ญ์‹œ์˜ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋‹ค์Œ ๋ฉ”์„œ๋“œ์˜ ์„ธ ๋ฒˆ์งธ ๋งค๊ฐœ ๋ณ€์ˆ˜๋Š” ๋ฌธ์ž์—ด ๋ฐฐ์—ด์ž…๋‹ˆ๋‹ค.

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

์ธ๋ผ์ธ Java UDF์— OBJECT ์ „๋‹ฌํ•˜๊ธฐยถ

๋‹ค์Œ ์˜ˆ์—์„œ๋Š” SQL OBJECT ๋ฐ์ดํ„ฐ ํƒ€์ž…๊ณผ ํ•ด๋‹น Java ๋ฐ์ดํ„ฐ ํƒ€์ž…(Map<String, String>)์„ ์‚ฌ์šฉํ•˜๊ณ  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

์ธ๋ผ์ธ Java UDF์— GEOGRAPHY ๊ฐ’ ์ „๋‹ฌํ•˜๊ธฐยถ

๋‹ค์Œ ์˜ˆ์—์„œ๋Š” 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 ์‹œ์Šคํ…œ ํŒจํ‚ค์ง€๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋•Œ Snowpark JAR ํŒŒ์ผ๋„ IMPORTS ์ ˆ์˜ ๊ฐ’์œผ๋กœ ํฌํ•จํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. 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  |
+----+--------------------------------------------------------+---------------------------------------------------------+--------+

์ธ๋ผ์ธ Java UDF์— VARIANT ๊ฐ’ ์ „๋‹ฌํ•˜๊ธฐยถ

SQL VARIANT ํ˜•์‹์˜ ๊ฐ’์„ Java UDF์— ์ „๋‹ฌํ•˜๋ฉด Snowflake๊ฐ€ Snowpark ํŒจํ‚ค์ง€ ์™€ ํ•จ๊ป˜ ์ œ๊ณต๋˜๋Š” ๋ฒ ๋ฆฌ์–ธํŠธ ํ˜•์‹์œผ๋กœ ๊ฐ’์„ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Variant ๋Š” Snowpark ํŒจํ‚ค์ง€ ๋ฒ„์ „ 1.4.0 ์ด์ƒ์—์„œ ์ง€์›๋ฉ๋‹ˆ๋‹ค.

Snowpark Variant ํ˜•์‹์€ Variant ์™€ ๋‹ค๋ฅธ ํ˜•์‹ ๊ฐ„์— ๊ฐ’์„ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

Snowpark Variant ํ˜•์‹์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด PACKAGES ์ ˆ์„ ์‚ฌ์šฉํ•˜์—ฌ UDF๋ฅผ ๋งŒ๋“ค ๋•Œ Snowpark ํŒจํ‚ค์ง€๋ฅผ ์ง€์ •ํ•˜์‹ญ์‹œ์˜ค. ์ด๋•Œ Snowpark JAR ํŒŒ์ผ๋„ IMPORTS ์ ˆ์˜ ๊ฐ’์œผ๋กœ ํฌํ•จํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. 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์ž…๋‹ˆ๋‹ค.

    • UDF ์†Œ์œ ์ž๊ฐ€ ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ๋Š” ํŒŒ์ผ์˜ ํŒŒ์ผ URL ๋˜๋Š” ๋ฌธ์ž์—ด ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค.

    ํŒŒ์ผ์€ ๋ช…๋ช…๋œ ๋‚ด๋ถ€ ์Šคํ…Œ์ด์ง€ ๋˜๋Š” ์™ธ๋ถ€ ์Šคํ…Œ์ด์ง€์— ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    ํŒŒ์ผ ํฌ๊ธฐ์™€ ๊ฐ™์€ ์ถ”๊ฐ€ ํŒŒ์ผ ํŠน์„ฑ์— ์‰ฝ๊ฒŒ ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    InputStream

    URL ํ˜•์‹:

    • ํ•จ์ˆ˜์˜ ํ˜ธ์ถœ์ž๊ฐ€ ์†Œ์œ ์ž์ด๊ธฐ๋„ ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ํŒŒ์ผ ์‚ฝ์ž… ๊ณต๊ฒฉ์˜ ์œ„ํ—˜์„ ์ค„์ด๊ธฐ ์œ„ํ•ด ๋ฒ”์œ„๊ฐ€ ์ง€์ •๋œ URL์ž…๋‹ˆ๋‹ค.

    ํŒŒ์ผ์€ ๋‚ด๋ถ€ ๋˜๋Š” ์™ธ๋ถ€ ์Šคํ…Œ์ด์ง€์— ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ „์ œ ์กฐ๊ฑดยถ

๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•˜์—ฌ ์ฝ”๋“œ์— ์Šคํ…Œ์ด์ง€์˜ ํŒŒ์ผ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผ Java ์ฒ˜๋ฆฌ๊ธฐ ์ฝ”๋“œ๊ฐ€ ํ•ด๋‹น ํŒŒ์ผ์„ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์ฒ˜๋ฆฌ๊ธฐ๊ฐ€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์Šคํ…Œ์ด์ง€๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

    ์™ธ๋ถ€ ์Šคํ…Œ์ด์ง€ ๋˜๋Š” ๋‚ด๋ถ€ ์Šคํ…Œ์ด์ง€๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๋ถ€ ์Šคํ…Œ์ด์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž ๋˜๋Š” ๋ช…๋ช…๋œ ์Šคํ…Œ์ด์ง€์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. Snowflake๋Š” ํ˜„์žฌ UDF ์ข…์† ํ•ญ๋ชฉ์— ํ…Œ์ด๋ธ” ์Šคํ…Œ์ด์ง€๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์Šคํ…Œ์ด์ง€ ์ƒ์„ฑ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ CREATE STAGE ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค. ๋‚ด๋ถ€ ์Šคํ…Œ์ด์ง€ ์œ ํ˜• ์„ ํƒ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋กœ์ปฌ ํŒŒ์ผ์„ ์œ„ํ•œ ๋‚ด๋ถ€ ์Šคํ…Œ์ด์ง€ ์„ ํƒํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

    ์Šคํ…Œ์ด์ง€์—์„œ ์ฝ๋Š” SQL ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์—ญํ• ์— ์Šคํ…Œ์ด์ง€์— ๋Œ€ํ•œ ์ ์ ˆํ•œ ๊ถŒํ•œ์„ ํ• ๋‹นํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์— ์œ ์˜ํ•˜์‹ญ์‹œ์˜ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์‚ฌ์šฉ์ž ์ •์˜ ํ•จ์ˆ˜์— ๋Œ€ํ•œ ๊ถŒํ•œ ๋ถ€์—ฌ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

  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 ํŒŒ์ผ์— ์ €์žฅ๋˜์—ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ๋‘ ๋ฒˆ์งธ ๋ฐ ์„ธ ๋ฒˆ์งธ ๊ฐ€์ ธ์˜จ ํŒŒ์ผ์ธ my_config_file_1.txt ๋ฐ my_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๋Š” ๋ณต์‚ฌ๋ณธ์„ ์žˆ๋Š” ๊ทธ๋Œ€๋กœ ์“ฐ๊ฑฐ๋‚˜, ํŒŒ์ผ์„ ์“ฐ๊ธฐ ์ „์— ๋‚ด์šฉ์˜ ์••์ถ•์„ ํ’€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์Šคํ…Œ์ด์ง€์˜ ํŒŒ์ผ์ด ์••์ถ•๋˜์–ด ์žˆ๊ณ  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โ€๋ฅผ ๋งŒ๋“ค์ง€ ๋ง๊ณ , ๋™์ผํ•œ CREATEFUNCTION ๋ช…๋ น์—์„œ โ€œMyData.txtโ€์™€ โ€œMyData.txt.gzโ€๋ฅผ ๋‘˜ ๋‹ค ๊ฐ€์ ธ์˜ค๋ ค๊ณ  ํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค.

  • ํŒŒ์ผ์„ ๋‘ ๋ฒˆ ์••์ถ•ํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํŒŒ์ผ์„ ์ˆ˜๋™์œผ๋กœ ์••์ถ•ํ•œ ๋‹ค์Œ, AUTO_COMPRESS=FALSE๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ํ•ด๋‹น ํŒŒ์ผ์„ PUT ํ•˜๋ฉด ํŒŒ์ผ์ด ๋‘ ๋ฒˆ์งธ๋กœ ์••์ถ•๋ฉ๋‹ˆ๋‹ค. ์Šค๋งˆํŠธ ์••์ถ• ํ’€๊ธฐ๋Š” ํ•œ ๋ฒˆ๋งŒ ์••์ถ•์„ ํ’€๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ(๋˜๋Š” JAR) ํŒŒ์ผ์€ UDF ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์ €์žฅ๋  ๋•Œ ์—ฌ์ „ํžˆ ์••์ถ•๋ฉ๋‹ˆ๋‹ค.

  • ์•ž์œผ๋กœ Snowflake๋Š” ์Šค๋งˆํŠธ ์••์ถ• ํ’€๊ธฐ๋ฅผ GZIP ์ด์™ธ์˜ ์••์ถ• ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ–ฅํ›„ ํ˜ธํ™˜์„ฑ ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ํ•˜๋ ค๋ฉด ์ด๋Ÿฌํ•œ ๋ชจ๋ฒ” ์‚ฌ๋ก€๋ฅผ ๋ชจ๋“  ํ˜•์‹์˜ ์••์ถ• ์‚ฌ์šฉ ํŒŒ์ผ์— ์ ์šฉํ•˜์‹ญ์‹œ์˜ค.

์ฐธ๊ณ 

JAR ํŒŒ์ผ์€ ์Šคํ…Œ์ด์ง€์—์„œ ์••์ถ• ๋˜๋Š” ๋น„์••์ถ• ํ˜•์‹์œผ๋กœ ์ €์žฅํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. Snowflake๋Š” ์••์ถ•๋œ ๋ชจ๋“  JAR ํŒŒ์ผ์„ Java UDF์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ž๋™์œผ๋กœ ์••์ถ•์„ ํ’‰๋‹ˆ๋‹ค.

SnowflakeFile ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋™์ ์œผ๋กœ ์ง€์ •๋œ ํŒŒ์ผ ์ฝ๊ธฐยถ

SnowflakeFile ํด๋ž˜์Šค์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Java ์ฒ˜๋ฆฌ๊ธฐ ์ฝ”๋“œ๋กœ ์Šคํ…Œ์ด์ง€์—์„œ ํŒŒ์ผ์„ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. SnowflakeFile ํด๋ž˜์Šค๋Š” Snowflake์˜ Java UDF ์ฒ˜๋ฆฌ๊ธฐ๊ฐ€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํด๋ž˜์Šค ๊ฒฝ๋กœ์— ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ 

ํŒŒ์ผ ์‚ฝ์ž… ๊ณต๊ฒฉ์— ๋Œ€ํ•œ ์ฝ”๋“œ์˜ ํƒ„๋ ฅ์„ฑ์„ ๋†’์ด๋ ค๋ฉด ํŒŒ์ผ์˜ ์œ„์น˜๋ฅผ UDF์— ์ „๋‹ฌํ•  ๋•Œ, ํŠนํžˆ ํ•จ์ˆ˜ ํ˜ธ์ถœ์ž๊ฐ€ ์†Œ์œ ์ž์ด๊ธฐ๋„ ํ•˜์ง€ ์•Š์„ ๋•Œ๋Š” ํ•ญ์ƒ ๋ฒ”์œ„๊ฐ€ ์ง€์ •๋œ URL์„ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค. ๊ธฐ๋ณธ ์ œ๊ณต ํ•จ์ˆ˜ BUILD_SCOPED_FILE_URL ์„ ์‚ฌ์šฉํ•˜์—ฌ SQL์—์„œ ๋ฒ”์œ„๊ฐ€ ์ง€์ •๋œ URL์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. BUILD_SCOPED_FILE_URL ์˜ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋น„์ •ํ˜• ๋ฐ์ดํ„ฐ ์†Œ๊ฐœ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

UDF ์ฝ”๋“œ๋ฅผ ๋กœ์ปฌ์—์„œ ๊ฐœ๋ฐœํ•˜๋ ค๋ฉด SnowflakeFile ์ด ํฌํ•จ๋œ Snowpark JAR์„ ์ฝ”๋“œ์˜ ํด๋ž˜์Šค ๊ฒฝ๋กœ์— ์ถ”๊ฐ€ํ•˜์‹ญ์‹œ์˜ค. snowpark.jar ์— ๋Œ€ํ•œ ์ •๋ณด๋Š” Snowpark Java๋ฅผ ์œ„ํ•œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ •ํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค. Snowpark ํด๋ผ์ด์–ธํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ด ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

SnowflakeFile ์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” CREATE FUNCTION ๋ฌธ์„ ํฌํ•จํ•œ SQL์—์„œ์ฒ˜๋Ÿผ 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

ํŒŒ์ผ ์‚ฝ์ž… ๊ณต๊ฒฉ ๊ฐ€๋Šฅ์„ฑ์„ ์ค„์ด๊ธฐ ์œ„ํ•ด ๋ฒ”์œ„๊ฐ€ ์ง€์ •๋œ URL์—์„œ ํŒŒ์ผ ์œ„์น˜๋ฅผ ์ „๋‹ฌํ•˜๋Š” UDF๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

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

์ฐธ๊ณ 

UDF ์†Œ์œ ์ž๋Š” ์œ„์น˜๊ฐ€ ๋ฒ”์œ„ ์ง€์ •๋œ URL์ด ์•„๋‹Œ ๋ชจ๋“  ํŒŒ์ผ์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฒ˜๋ฆฌ๊ธฐ ์ฝ”๋“œ๊ฐ€ ์ƒˆ 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 ์„ ์‚ฌ์šฉํ•˜์—ฌ SQL์—์„œ ๋ฒ”์œ„๊ฐ€ ์ง€์ •๋œ URL์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 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

ํŒŒ์ผ ์‚ฝ์ž… ๊ณต๊ฒฉ ๊ฐ€๋Šฅ์„ฑ์„ ์ค„์ด๊ธฐ ์œ„ํ•ด ๋ฒ”์œ„๊ฐ€ ์ง€์ •๋œ URL์—์„œ ํŒŒ์ผ ์œ„์น˜๋ฅผ ์ „๋‹ฌํ•˜๋Š” UDF๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

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์—์„œ jar_stage ๋ผ๋Š” ์Šคํ…Œ์ด์ง€๋ฅผ ์ƒ์„ฑํ•˜์—ฌ, UDF ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ํฌํ•จ๋œ JAR ํŒŒ์ผ์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

    ์Šคํ…Œ์ด์ง€ ์ƒ์„ฑ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ 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);
Copy
+----------------------+
| DECREMENT_VALUE(-15) |
|----------------------|
|                  -16 |
+----------------------+