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;
}
}';
UDF๋ฅผ ํธ์ถํฉ๋๋ค.
SELECT echo_varchar('Hello');
+-----------------------+
| ECHO_VARCHAR('HELLO') |
|-----------------------|
| Hello |
+-----------------------+
์ธ๋ผ์ธ Java UDF์ NULL ์ ๋ฌํ๊ธฐยถ
์ด๋ ์์์ ์ ์ํ echo_varchar()
UDF๋ฅผ ์ฌ์ฉํฉ๋๋ค. SQL NULL
๊ฐ์ ์์์ ์ผ๋ก Java null
๋ก ๋ณํ๋๊ณ ํด๋น Java null
์ด ๋ฐํ๋์ด ์์์ ์ผ๋ก SQL NULL
๋ก ๋ค์ ๋ณํ๋ฉ๋๋ค.
UDF๋ฅผ ํธ์ถํฉ๋๋ค.
SELECT echo_varchar(NULL);
+--------------------+
| ECHO_VARCHAR(NULL) |
|--------------------|
| NULL |
+--------------------+
๋ฐฐ์ด ๊ฐ ์ ๋ฌํ๊ธฐยถ
Java ๋ฉ์๋๋ ๋ค์ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ ์ค ํ๋๋ก SQL ๋ฐฐ์ด์ ์์ ํ ์ ์์ต๋๋ค.
Java์ ๋ฐฐ์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํฉ๋๋ค.
Java์ varargs (๊ฐ๋ณ ์ธ์ ์) ๊ธฐ๋ฅ์ ์ฌ์ฉํฉ๋๋ค.
๋ ๊ฒฝ์ฐ ๋ชจ๋ SQL ์ฝ๋๋ ARRAY ๋ฅผ ์ ๋ฌํด์ผ ํฉ๋๋ค.
์ฐธ๊ณ
SQL ์ ํ์ ์ ํจํ ๋งคํ๊ณผ ํจ๊ป Java ์ ํ์ ์ฌ์ฉํด์ผ ํฉ๋๋ค. ์์ธํ ๋ด์ฉ์ SQL-Java ๋ฐ์ดํฐ ํ์ ๋งคํ ์น์ ์ ์ฐธ์กฐํ์ญ์์ค.
ARRAY๋ฅผ ํตํด ์ ๋ฌยถ
Java ๋งค๊ฐ ๋ณ์๋ฅผ ๋ฐฐ์ด๋ก ์ ์ธํ์ญ์์ค. ์๋ฅผ ๋ค์ด, ๋ค์ ๋ฉ์๋์ ์ธ ๋ฒ์งธ ๋งค๊ฐ ๋ณ์๋ ๋ฌธ์์ด ๋ฐฐ์ด์ ๋๋ค.
static int myMethod(int fixedArgument1, int fixedArgument2, String[] stringArray)
๋ค์์ ์์ ํ ์์ ๋๋ค.
ํ ์ด๋ธ์ ๋ง๋ค๊ณ ๋ก๋ฉํฉ๋๋ค.
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');
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);
}
}
$$;
UDF๋ฅผ ํธ์ถํฉ๋๋ค.
SELECT concat_varchar_2(a)
FROM string_array_table
ORDER BY id;
+---------------------+
| CONCAT_VARCHAR_2(A) |
|---------------------|
| Hello |
| Hello Jay |
| Hello Jay Smith |
+---------------------+
Varargs๋ฅผ ํตํด ์ ๋ฌํ๊ธฐยถ
varargs๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋ฐฐ์ด์ ์ฌ์ฉํ๋ ๊ฒ๊ณผ ๋งค์ฐ ์ ์ฌํฉ๋๋ค.
Java ์ฝ๋์์ Java์ varargs ์ ์ธ ์คํ์ผ์ ์ฌ์ฉํ์ญ์์ค.
static int myMethod(int fixedArgument1, int fixedArgument2, String ... stringArray)
๋ค์์ ์์ ํ ์์ ๋๋ค. ์ด ์์ ์ด์ ์(๋ฐฐ์ด์ ๊ฒฝ์ฐ) ๊ฐ์ ์ ์ผํ ์ค์ํ ์ฐจ์ด์ ์ ๋ฉ์๋์ ๋ํ ๋งค๊ฐ ๋ณ์ ์ ์ธ์ ๋๋ค.
ํ ์ด๋ธ์ ๋ง๋ค๊ณ ๋ก๋ฉํฉ๋๋ค.
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');
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);
}
}
$$;
UDF๋ฅผ ํธ์ถํฉ๋๋ค.
SELECT concat_varchar(a)
FROM string_array_table
ORDER BY id;
+-------------------+
| CONCAT_VARCHAR(A) |
|-------------------|
| Hello |
| Hello Jay |
| Hello Jay Smith |
+-------------------+
์ธ๋ผ์ธ 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;
}
}
$$;
UDF๋ฅผ ํธ์ถํฉ๋๋ค.
SELECT return_a_null();
+-----------------+
| RETURN_A_NULL() |
|-----------------|
| NULL |
+-----------------+
์ธ๋ผ์ธ 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"} }');
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);
}
}
$$;
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" | |
| } | |
+-------------------------------------+--------------------------------------------------+
์ธ๋ผ์ธ 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);
}
}
$$;
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;
์ถ๋ ฅ์ ๋ค์๊ณผ ์ ์ฌํฉ๋๋ค.
+----+--------------------------------------------------------+---------------------------------------------------------+--------+
| 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;
}
}
$$;
Java UDF๋ก ํ์ผ ์ฝ๊ธฐยถ
์ฒ๋ฆฌ๊ธฐ ์ฝ๋๋ก ํ์ผ์ ๋ด์ฉ์ ์ฝ์ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ์ฒ๋ฆฌ๊ธฐ๋ก ๋น์ ํ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด ํ์ผ์ ์ฝ์ ์ ์์ต๋๋ค. ๋น์ ํ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ ๋ํ ์์ธํ ๋ด์ฉ๊ณผ ์์ ์ฝ๋๋ UDF ๋ฐ ํ๋ก์์ ์ฒ๋ฆฌ๊ธฐ๋ก ๋น์ ํ ๋ฐ์ดํฐ ์ฒ๋ฆฌํ๊ธฐ ์น์ ์ ์ฐธ์กฐํ์ญ์์ค.
ํ์ผ์ ์ฒ๋ฆฌ๊ธฐ์ ์ฌ์ฉํ ์ ์๋ Snowflake ์คํ ์ด์ง์ ์์ด์ผ ํฉ๋๋ค.
์คํ ์ด์ง๋ ํ์ผ์ ๋ด์ฉ์ ์ฝ๊ธฐ ์ํด ์ฒ๋ฆฌ๊ธฐ๋ ๋ค์์ ์ํํ ์ ์์ต๋๋ค.
ํ์ผ ๊ฒฝ๋ก๊ฐ IMPORTS ์ ์ ์ ์ ์ผ๋ก ์ง์ ๋ ํ์ผ์ ์ฝ์ต๋๋ค. ๋ฐํ์์ ์ฝ๋๋ UDF์ ํ ๋๋ ํฐ๋ฆฌ์์ ํ์ผ์ ์ฝ์ต๋๋ค.
์ด๋ ์ด๊ธฐํ ์ค์ ํ์ผ์ ์ ์ ์ผ๋ก ์ก์ธ์คํ๋ ค ํ ๋ ์ ์ฉํ ์ ์์ต๋๋ค.
SnowflakeFile
ํด๋์ค ๋๋InputStream
ํด๋์ค์ ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ๋์ ์ผ๋ก ์ง์ ๋ ํ์ผ์ ์ฝ์ต๋๋ค.ํธ์ถ์๊ฐ ์ง์ ํ ํ์ผ์ ์ก์ธ์คํด์ผ ํ๋ ๊ฒฝ์ฐ ์ด๋ ๊ฒ ํ ์ ์์ต๋๋ค. ์์ธํ ๋ด์ฉ์ ์ด ํญ๋ชฉ์ ๋ค์ ์น์ ์ ์ฐธ์กฐํ์ญ์์ค.
SnowflakeFile ์ ์ฌ์ฉํ์ฌ ๋์ ์ผ๋ก ์ง์ ๋ ํ์ผ ์ฝ๊ธฐ
InputStream ์ ์ฌ์ฉํ์ฌ ๋์ ์ผ๋ก ์ง์ ๋ ํ์ผ ์ฝ๊ธฐ
SnowflakeFile
์ ๋ค์ ํ์ ์ค๋ช ๋ ๊ฒ์ฒ๋ผInputStream
๊ณผ ํจ๊ป ์ฌ์ฉํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.ํด๋์ค
์ ๋ ฅ
์ฐธ๊ณ
SnowflakeFile
URL ํ์:
ํจ์์ ํธ์ถ์๊ฐ ์์ ์์ด๊ธฐ๋ ํ์ง ์์ ๊ฒฝ์ฐ ํ์ผ ์ฝ์ ๊ณต๊ฒฉ์ ์ํ์ ์ค์ด๊ธฐ ์ํด ๋ฒ์๊ฐ ์ง์ ๋ URL์ ๋๋ค.
UDF ์์ ์๊ฐ ์ก์ธ์คํ ์ ์๋ ํ์ผ์ ํ์ผ URL ๋๋ ๋ฌธ์์ด ๊ฒฝ๋ก์ ๋๋ค.
ํ์ผ์ ๋ช ๋ช ๋ ๋ด๋ถ ์คํ ์ด์ง ๋๋ ์ธ๋ถ ์คํ ์ด์ง์ ์์ด์ผ ํฉ๋๋ค.
ํ์ผ ํฌ๊ธฐ์ ๊ฐ์ ์ถ๊ฐ ํ์ผ ํน์ฑ์ ์ฝ๊ฒ ์ก์ธ์คํ ์ ์์ต๋๋ค.
InputStream
URL ํ์:
ํจ์์ ํธ์ถ์๊ฐ ์์ ์์ด๊ธฐ๋ ํ์ง ์์ ๊ฒฝ์ฐ ํ์ผ ์ฝ์ ๊ณต๊ฒฉ์ ์ํ์ ์ค์ด๊ธฐ ์ํด ๋ฒ์๊ฐ ์ง์ ๋ URL์ ๋๋ค.
ํ์ผ์ ๋ด๋ถ ๋๋ ์ธ๋ถ ์คํ ์ด์ง์ ์์ด์ผ ํฉ๋๋ค.
์ ์ ์กฐ๊ฑดยถ
๋ค์์ ์ํํ์ฌ ์ฝ๋์ ์คํ ์ด์ง์ ํ์ผ์ ์ฌ์ฉํ ์ ์๋๋ก ํด์ผ Java ์ฒ๋ฆฌ๊ธฐ ์ฝ๋๊ฐ ํด๋น ํ์ผ์ ์ฝ์ ์ ์์ต๋๋ค.
์ฒ๋ฆฌ๊ธฐ๊ฐ ์ฌ์ฉํ ์ ์๋ ์คํ ์ด์ง๋ฅผ ๋ง๋ญ๋๋ค.
์ธ๋ถ ์คํ ์ด์ง ๋๋ ๋ด๋ถ ์คํ ์ด์ง๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๋ด๋ถ ์คํ ์ด์ง๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์ฌ์ฉ์ ๋๋ ๋ช ๋ช ๋ ์คํ ์ด์ง์ฌ์ผ ํฉ๋๋ค. Snowflake๋ ํ์ฌ UDF ์ข ์ ํญ๋ชฉ์ ํ ์ด๋ธ ์คํ ์ด์ง๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ์ง์ํ์ง ์์ต๋๋ค. ์คํ ์ด์ง ์์ฑ์ ๋ํ ์์ธํ ๋ด์ฉ์ CREATE STAGE ์น์ ์ ์ฐธ์กฐํ์ญ์์ค. ๋ด๋ถ ์คํ ์ด์ง ์ ํ ์ ํ์ ๋ํ ์์ธํ ๋ด์ฉ์ ๋ก์ปฌ ํ์ผ์ ์ํ ๋ด๋ถ ์คํ ์ด์ง ์ ํํ๊ธฐ ์น์ ์ ์ฐธ์กฐํ์ญ์์ค.
์คํ ์ด์ง์์ ์ฝ๋ SQL ์์ ์ ์ํํ๋ ์ญํ ์ ์คํ ์ด์ง์ ๋ํ ์ ์ ํ ๊ถํ์ ํ ๋นํด์ผ ํ๋ค๋ ์ ์ ์ ์ํ์ญ์์ค. ์์ธํ ๋ด์ฉ์ ์ฌ์ฉ์ ์ ์ ํจ์์ ๋ํ ๊ถํ ๋ถ์ฌ ์น์ ์ ์ฐธ์กฐํ์ญ์์ค.
์ฝ๋๋ก ์ฝ์ ํ์ผ์ ์คํ ์ด์ง์ ๋ณต์ฌํฉ๋๋ค.
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();
}
}
๋ค์ 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';
์ด ์ฝ๋๋ UDF๋ฅผ ํธ์ถํฉ๋๋ค.
SELECT file_reader('my_config_file_1.txt') ...;
...
SELECT file_reader('my_config_file_2.txt') ...;
์์ถ ๋๋ ๋น์์ถ ํ์์ ํ์ผ ์ก์ธ์ค ์ฌ๋ถ ์ ํํ๊ธฐยถ
์คํ ์ด์ง์ ํ์ผ์ ์์ถ ๋๋ ๋น์์ถ ํ์์ผ๋ก ์ ์ฅํ ์ ์์ต๋๋ค. ์ฌ์ฉ์๋ ํ์ผ์ ์คํ ์ด์ง์ ๋ณต์ฌํ๊ธฐ ์ ์ ์์ถํ๊ฑฐ๋, PUT ๋ช ๋ น์ ํ์ผ ์์ถ์ ์ง์ํ ์ ์์ต๋๋ค.
Snowflake๊ฐ GZIP ํ์์ผ๋ก ์์ถ๋ ํ์ผ์ ์คํ ์ด์ง์์ UDF ํ ๋๋ ํฐ๋ฆฌ๋ก ๋ณต์ฌํ ๋ Snowflake๋ ๋ณต์ฌ๋ณธ์ ์๋ ๊ทธ๋๋ก ์ฐ๊ฑฐ๋, ํ์ผ์ ์ฐ๊ธฐ ์ ์ ๋ด์ฉ์ ์์ถ์ ํ ์ ์์ต๋๋ค.
์คํ ์ด์ง์ ํ์ผ์ด ์์ถ๋์ด ์๊ณ UDF ํ ๋๋ ํฐ๋ฆฌ์ ๋ณต์ฌ๋ณธ๋ ์์ถํ๋ ค๋ ๊ฒฝ์ฐ, IMPORTS ์ ์ ํ์ผ ์ด๋ฆ์ ์ง์ ํ ๋ ์๋ ํ์ผ ์ด๋ฆ(์: โMyData.txt.gzโ)์ IMPORTS ์ ์ ์ฌ์ฉํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค. ์:
... IMPORTS = ('@MyStage/MyData.txt.gz', ...)
์คํ ์ด์ง์ ์๋ ํ์ผ์ด 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;
}
}
$$;
ํ์ผ ์ฝ์ ๊ณต๊ฒฉ ๊ฐ๋ฅ์ฑ์ ์ค์ด๊ธฐ ์ํด ๋ฒ์๊ฐ ์ง์ ๋ URL์์ ํ์ผ ์์น๋ฅผ ์ ๋ฌํ๋ UDF๋ฅผ ํธ์ถํฉ๋๋ค.
SELECT sum_total_sales(BUILD_SCOPED_FILE_URL('@sales_data_stage', '/car_sales.json'));
์ฐธ๊ณ
UDF ์์ ์๋ ์์น๊ฐ ๋ฒ์ ์ง์ ๋ URL์ด ์๋ ๋ชจ๋ ํ์ผ์ ์ก์ธ์คํ ์ ์์ด์ผ ํฉ๋๋ค. ์ฒ๋ฆฌ๊ธฐ ์ฝ๋๊ฐ ์ requireScopedUrl
๋งค๊ฐ ๋ณ์์ ๋ํ boolean
๊ฐ์ผ๋ก SnowflakeFile.newInstance
๋ฉ์๋๋ฅผ ํธ์ถํ๋๋ก ํ์ฌ ์ด๋ค ์คํ
์ด์ง๋ ํ์ผ์ ์ฝ์ ์ ์์ต๋๋ค.
๋ค์ ์์์๋ ๋ฒ์๊ฐ ์ง์ ๋ URL์ด ํ์ํ์ง ์์์ ์ง์ ํ๋ฉด์ SnowflakeFile.newInstance
๋ฅผ ์ฌ์ฉํฉ๋๋ค.
String filename = "@my_stage/filename.txt";
String sfFile = SnowflakeFile.newInstance(filename, false);
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;
}
}
$$;
ํ์ผ ์ฝ์ ๊ณต๊ฒฉ ๊ฐ๋ฅ์ฑ์ ์ค์ด๊ธฐ ์ํด ๋ฒ์๊ฐ ์ง์ ๋ URL์์ ํ์ผ ์์น๋ฅผ ์ ๋ฌํ๋ UDF๋ฅผ ํธ์ถํฉ๋๋ค.
SELECT sum_total_sales(BUILD_SCOPED_FILE_URL('@sales_data_stage', '/car_sales.json'));
๋จ์ํ ์คํ ์ด์ง๋ Java UDF ์์ฑ ๋ฐ ํธ์ถํ๊ธฐยถ
๋ค์ ๋ฌธ์ ๊ฐ๋จํ Java UDF๋ฅผ ๋ง๋ญ๋๋ค. ์ผ๋ฐ์ ์ผ๋ก ์ด ์ํ์ ํ์ผ ์ ๋ฆฌํ๊ธฐ ์ ์ค๋ช ๋ ํ์ผ ๋ฐ ๋๋ ํฐ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋ฐ๋ฆ ๋๋ค.
Java ์ฒ๋ฆฌ๊ธฐ ์ฝ๋ ์์ฑ ๋ฐ ์ปดํ์ผํ๊ธฐยถ
ํ๋ก์ ํธ์ ๋ฃจํธ ๋๋ ํฐ๋ฆฌ(์ด ๊ฒฝ์ฐ
my_udf
)์์ ์์ค .java ํ์ผ์ ๋ณด๊ดํsrc
ํ์ ๋๋ ํฐ๋ฆฌ, ๊ทธ๋ฆฌ๊ณ ์์ฑ๋ .class ํ์ผ์ ๋ณด๊ดํclasses
ํ์ ๋๋ ํฐ๋ฆฌ๋ฅผ ์์ฑํฉ๋๋ค.๋ค์๊ณผ ์ ์ฌํ ๋๋ ํฐ๋ฆฌ ๊ณ์ธต ๊ตฌ์กฐ๊ฐ ์์ด์ผ ํฉ๋๋ค.
my_udf/ |-- classes/ |-- src/
src
๋๋ ํฐ๋ฆฌ์์, ํด๋์ค๊ฐmypackage
ํจํค์ง์ ์๋ .java ํ์ผ์ ๋ณด์ ํmypackage
๋ผ๋ ๋๋ ํฐ๋ฆฌ๋ฅผ ์์ฑํฉ๋๋ค.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."); } }
ํ๋ก์ ํธ ๋ฃจํธ ๋๋ ํฐ๋ฆฌ(์ด ๊ฒฝ์ฐ
my_udf
)์์javac
๋ช ๋ น์ ์ฌ์ฉํ์ฌ ์์ค ์ฝ๋๋ฅผ ์ปดํ์ผํฉ๋๋ค.๋ค์ ์์
javac
๋ช ๋ น์MyUDFHandler.java
๋ฅผ ์ปดํ์ผํ์ฌclasses
๋๋ ํฐ๋ฆฌ์MyUDFHandler.class
ํ์ผ์ ์์ฑํฉ๋๋ค.javac -d classes src/mypackage/MyUDFHandler.java
์ด ์์๋ ๋ค์ ์ธ์๊ฐ ํฌํจ๋ฉ๋๋ค.
-d classes
โ ์์ฑ๋ ํด๋์ค ํ์ผ์ด ๊ธฐ๋ก๋์ด์ผ ํ๋ ๋๋ ํฐ๋ฆฌ.src/mypackage/MyUDFHandler.java
โ ํ์์ .java ํ์ผ ๊ฒฝ๋ก:source_directory/package_directory/Java_file_name
.
์ปดํ์ผ๋ ์ฝ๋๋ฅผ JAR ํ์ผ๋ก ํจํค์งํ๊ธฐยถ
์ ํ์ ์ผ๋ก, ํ๋ก์ ํธ ๋ฃจํธ ๋๋ ํฐ๋ฆฌ์์, ๋ค์ ์์ฑ์ ํฌํจํ๋
my_udf.manifest
๋ผ๋ ๋งค๋ํ์คํธ ํ์ผ์ ์์ฑํฉ๋๋ค.Manifest-Version: 1.0 Main-Class: mypackage.MyUDFHandler
ํ๋ก์ ํธ ๋ฃจํธ ๋๋ ํฐ๋ฆฌ์์
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
์ด ์์๋ ๋ค์ ์ธ์๊ฐ ํฌํจ๋ฉ๋๋ค.
cmf
โ ๋ช ๋ น ์ธ์:c
๋ JAR ํ์ผ์ ์์ฑํ๊ณ ,m
์ ์ง์ ๋ .manifest ํ์ผ์ ์ฌ์ฉํ๊ณ ,f
๋ ์ง์ ๋ ์ด๋ฆ์ JAR ํ์ผ์ ๋ถ์ฌํฉ๋๋ค.my_udf.manifest
โ ๋งค๋ํ์คํธ ํ์ผ.my_udf.jar
โ ์์ฑํ JAR ํ์ผ์ ์ด๋ฆ.-C ./classes
โ ์์ฑ๋ .class ํ์ผ์ ํฌํจํ๋ ๋๋ ํฐ๋ฆฌ.mypackage/MyUDFHandler.class
โ JAR์ ํฌํจํ .class ํ์ผ์ ํจํค์ง ๋ฐ ์ด๋ฆ.
์ปดํ์ผ๋ ์ฒ๋ฆฌ๊ธฐ๊ฐ ์๋ JAR ํ์ผ์ ์คํ ์ด์ง์ ์ ๋ก๋ํ๊ธฐยถ
Snowflake์์
jar_stage
๋ผ๋ ์คํ ์ด์ง๋ฅผ ์์ฑํ์ฌ, UDF ํธ๋ค๋ฌ๊ฐ ํฌํจ๋ JAR ํ์ผ์ ์ ์ฅํฉ๋๋ค.์คํ ์ด์ง ์์ฑ์ ๋ํ ์์ธํ ๋ด์ฉ์ CREATE STAGE ์น์ ์ ์ฐธ์กฐํ์ญ์์ค.
PUT
๋ช ๋ น์ ์ฌ์ฉํ์ฌ JAR ํ์ผ์ ๋ก์ปฌ ํ์ผ ์์คํ ์์ ์คํ ์ด์ง๋ก ๋ณต์ฌํ์ญ์์ค.put file:///Users/Me/my_udf/my_udf.jar @jar_stage auto_compress = false overwrite = true ;
PUT
๋ช ๋ น์ ์คํฌ๋ฆฝํธ ํ์ผ์ ์ ์ฅํ ๋ค์, SnowSQL ์ ํตํด ํด๋น ํ์ผ์ ์คํํ ์ ์์ต๋๋ค.snowsql
๋ช ๋ น์ ๋ค์๊ณผ ์ ์ฌํฉ๋๋ค.snowsql -a <account_identifier> -w <warehouse> -d <database> -s <schema> -u <user> -f put_command.sql
์ด ์์์๋ ์ฌ์ฉ์์ ๋น๋ฐ๋ฒํธ๊ฐ 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'
;
UDF๋ฅผ ํธ์ถํฉ๋๋ค.
SELECT decrement_value(-15);
+----------------------+
| DECREMENT_VALUE(-15) |
|----------------------|
| -16 |
+----------------------+