このトピックでは、コード埋め込みユーザー定義関数 (UDF) を使用して、JavaまたはPythonコードをSQLスクリプトに埋め込む方法について説明します。
背景情報
MaxComputeのコード埋め込みUDFは、コードの実装とメンテナンスにおける次の問題を解決します。
複雑なコードの実装: UDFを作成してコードを開発した後、Javaでコードをコンパイルし、リソースと関数を作成する必要があります。
不便なコードメンテナンス: SQLスクリプトで参照されているUDFの実装ロジックを直接表示したり、JARパッケージのソースコードを取得したりすることはできません。
コードの読みやすさが悪い: ユーザー定義型 (UDT) を使用してJavaライブラリ関数を実装するには、Javaコードを長いコード行の式に変換する必要があります。 さらに、Javaコードを式として記述できない場合があります。 例:
Foo f = new Foo(); f.execute(); f.getResult();
説明
コード埋め込みUDFを使用すると、JavaまたはPythonコードをSQLスクリプトに埋め込むことができます。 スクリプトをコンパイルすると、Janinoコンパイラは埋め込みコードを識別して抽出し、Javaでコードをコンパイルしてから、リソースを動的に生成し、一時関数を作成します。
SQLスクリプトとサードパーティのコード行を同じソースコードファイルに配置できます。 これにより、UDTまたはUDFの使用が簡単になり、日々の開発とメンテナンスが容易になります。
制限事項
組み込みJavaコードのコンパイルには、Janinoコンパイラのみを使用できます。 埋め込みJavaコードの構文は、標準JDK構文のサブセットでなければなりません。 埋め込みJavaコードには次の制限があります。
Lambda式はサポートされていません。
1つのcatchブロックに複数の種類の例外を指定することはできません。 たとえば、
catch(Exception1 | Exception2 e)
は使用できません。ジェネリック引数を自動的に推論することはできません。 たとえば、
Map map = new HashMap<>();
はサポートされていません。型引数の推論の式は無視されます。
(String) myMap.get(key)
などの引数型を指定するには、キャスト式を使用する必要があります。Java仮想マシン (JVM) の -eaオプションが使用されている場合でも、アサーションは強制的に有効になります。
Java 8以降のバージョンでプログラムされているコードはサポートされていません。
UDTの参照埋め込みコード
次のコードは例を提供します。 SQL文をスクリプトモードで送信する必要があります。 詳細については、「スクリプトモードのSQL」をご参照ください。
SELECT
s,
com.mypackage.Foo.extractNumber(s)
FROM VALUES ('abc123def'),('apple') AS t(s);
#CODE ('lang'='JAVA')
package com.mypackage;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Foo {
final static Pattern compile = Pattern.compile(".*?([0-9]+).*");
public static String extractNumber(String input) {
final Matcher m = compile.matcher(input);
if (m.find()) {
return m.group(1);
}
return null;
}
}
#END CODE;
#CODE
は、埋め込みコードブロックの先頭を示します。#END CODE
は、埋め込みコードブロックの終わりを示します。 この例では、埋め込みコードブロックはスクリプトの最後に配置され、スクリプト全体に適用されます。'lang'='JAVA
は、埋め込みコードがJavaであることを示します。 PYTHONでコードをコンパイルする場合、JAVAはPython
に置き換えることができます。SQLスクリプトでUDT構文を使用して、
Foo.extractNumber
を呼び出すことができます。
Javaコード埋め込みUDFの定義と呼び出し
次のコードは例を提供します。 SQL文をスクリプトモードで送信する必要があります。 詳細については、「スクリプトモードのSQL」をご参照ください。
CREATE TEMPORARY FUNCTION foo AS 'com.mypackage.Reverse' USING
#CODE ('lang'='JAVA')
package com.mypackage;
import com.aliyun.odps.udf.UDF;
public class Reverse extends UDF {
public String evaluate(String input) {
if (input == null) return null;
StringBuilder ret = new StringBuilder();
for (int i = input.toCharArray().length - 1; i >= 0; i--) {
ret.append(input.toCharArray()[i]);
}
return ret.toString();
}
}
#END CODE;
SELECT foo('abdc');
埋め込みコードブロックは、
USING
の後、またはスクリプトの最後に配置できます。USING
の後に配置されたコードブロックは、CREATE TEMPORARY FUNCTION
ステートメントにのみ適用されます。CREATE TEMPORARY function
によって作成された関数は一時的な関数です。 この一時関数は、現在の実行プロセス中にのみ有効になり、MaxComputeメタデータシステムには保存されません。 永続関数を作成してMaxComputeメタデータシステムに保存する方法の詳細については、「create SQL function」をご参照ください。
Javaコード埋め込みUDTFの定義と呼び出し
次のコードは例を提供します。 SQL文をスクリプトモードで送信する必要があります。 詳細については、「スクリプトモードのSQL」をご参照ください。
CREATE TEMPORARY FUNCTION foo AS 'com.mypackage.Reverse' USING
#CODE ('lang'='JAVA', 'filename'='embedded.jar')
package com.mypackage;
import com.aliyun.odps.udf.UDTF;
import com.aliyun.odps.udf.UDFException;
import com.aliyun.odps.udf.annotation.Resolve;
@Resolve({"string->string,string"})
public class Reverse extends UDTF {
@Override
public void process(Object[] objects) throws UDFException {
String str = (String) objects[0];
String[] split = str.split(",");
forward(split[0], split[1]);
}
}
#END CODE;
SELECT foo('ab,dc') AS (a,b);
@ Resolve
の戻り値は、string[]
型である必要があります。 ただし、Janinoコンパイラは、"string->string,string"
をstring[]
として識別できません。 この問題に対処するには、@ Resolve
によってコメントされたパラメーターを {}
で囲む必要があります。 一般的な方法を使用してJava UDTFを作成する場合、中括弧 {}
は必要ありません。
Pythonコード埋め込みUDFの定義と呼び出し
次のコードは例を提供します。 SQL文をスクリプトモードで送信する必要があります。 詳細については、「スクリプトモードのSQL」をご参照ください。
CREATE TEMPORARY FUNCTION foo AS 'embedded.UDFTest' USING
#CODE ('lang'='PYTHON', 'filename'='embedded')
from odps.udf import annotate
@annotate("bigint->bigint")
class UDFTest(object):
def evaluate(self, a):
return a * a
#END CODE;
SELECT foo(4);
Pythonコードのインデントは、Python言語の仕様に準拠している必要があります。
Python UDFを作成するときは、
AS
句に続くクラス名にPythonソースコードのファイル名を含める必要があります。'filename'='embedded'
を使用して、仮想ファイル名を指定できます。さまざまなバージョンのPythonの開発と使用の詳細については、次のトピックを参照してください。
Python 2: Python 2でUDFを開発する
Python 3: Python 3でUDFを開発する