This is an automated email from the ASF dual-hosted git repository.
qiaojialin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new 9327fc4 [IOTDB-1071] Built-in UDF registration service (#2380)
9327fc4 is described below
commit 9327fc49950d7b902b492d8fa05835c80a78e94b
Author: Steve Yurong Su <[email protected]>
AuthorDate: Wed Dec 30 20:02:40 2020 +0800
[IOTDB-1071] Built-in UDF registration service (#2380)
---
.../org/apache/iotdb/db/conf/IoTDBConstant.java | 11 +-
.../apache/iotdb/db/qp/constant/SQLConstant.java | 10 ++
.../apache/iotdb/db/qp/executor/PlanExecutor.java | 62 +++++++-
.../apache/iotdb/db/query/dataset/ListDataSet.java | 12 +-
.../BuiltinFunction.java} | 100 ++++++-------
.../udf/service/UDFRegistrationInformation.java | 25 +++-
.../query/udf/service/UDFRegistrationService.java | 137 ++++++++++++-----
.../iotdb/db/integration/IoTDBUDFManagementIT.java | 162 +++++++++++++++++++--
8 files changed, 397 insertions(+), 122 deletions(-)
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConstant.java
b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConstant.java
index 679bc85..edd8607 100644
--- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConstant.java
+++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConstant.java
@@ -86,8 +86,15 @@ public class IoTDBConstant {
public static final String COLUMN_CANCELLED = "cancelled";
public static final String COLUMN_DONE = "done";
- public static final String COLUMN_FUNCTION_NAME = "UDF name";
- public static final String COLUMN_FUNCTION_CLASS = "class name";
+ public static final String COLUMN_FUNCTION_NAME = "function name";
+ public static final String COLUMN_FUNCTION_TYPE = "function type";
+ public static final String COLUMN_FUNCTION_CLASS = "class name (UDF)";
+
+ public static final String FUNCTION_TYPE_NATIVE = "native";
+ public static final String FUNCTION_TYPE_BUILTIN_UDAF = "built-in UDAF";
+ public static final String FUNCTION_TYPE_BUILTIN_UDTF = "built-in UDTF";
+ public static final String FUNCTION_TYPE_EXTERNAL_UDAF = "external UDAF";
+ public static final String FUNCTION_TYPE_EXTERNAL_UDTF = "external UDTF";
public static final String PATH_WILDCARD = "*";
public static final String TIME = "time";
diff --git
a/server/src/main/java/org/apache/iotdb/db/qp/constant/SQLConstant.java
b/server/src/main/java/org/apache/iotdb/db/qp/constant/SQLConstant.java
index 3109af4..ecbc667 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/constant/SQLConstant.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/constant/SQLConstant.java
@@ -18,8 +18,11 @@
*/
package org.apache.iotdb.db.qp.constant;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
import org.apache.iotdb.db.metadata.PartialPath;
import org.apache.iotdb.db.qp.sql.SqlBaseLexer;
@@ -70,6 +73,9 @@ public class SQLConstant {
public static final String ALL = "all";
+ private static final Set<String> NATIVE_FUNCTION_NAMES = new
HashSet<>(Arrays.asList(
+ MIN_TIME, MAX_TIME, MIN_VALUE, MAX_VALUE, FIRST_VALUE, LAST_VALUE,
COUNT, SUM, AVG));
+
public static final int KW_AND = 1;
public static final int KW_OR = 2;
public static final int KW_NOT = 3;
@@ -265,4 +271,8 @@ public class SQLConstant {
public static boolean isReservedPath(PartialPath pathStr) {
return pathStr.equals(TIME_PATH);
}
+
+ public static Set<String> getNativeFunctionNames() {
+ return NATIVE_FUNCTION_NAMES;
+ }
}
diff --git
a/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java
b/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java
index 7313109..7130bdb 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java
@@ -27,6 +27,7 @@ import static
org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_DEVICES;
import static org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_DONE;
import static org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_FUNCTION_CLASS;
import static org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_FUNCTION_NAME;
+import static org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_FUNCTION_TYPE;
import static org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_ITEM;
import static org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_PRIVILEGE;
import static org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_PROGRESS;
@@ -36,10 +37,16 @@ import static
org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_TASK_NAME;
import static org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_TTL;
import static org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_USER;
import static org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_VALUE;
+import static
org.apache.iotdb.db.conf.IoTDBConstant.FUNCTION_TYPE_BUILTIN_UDAF;
+import static
org.apache.iotdb.db.conf.IoTDBConstant.FUNCTION_TYPE_BUILTIN_UDTF;
+import static org.apache.iotdb.db.conf.IoTDBConstant.FUNCTION_TYPE_NATIVE;
+import static
org.apache.iotdb.db.conf.IoTDBConstant.FUNCTION_TYPE_EXTERNAL_UDAF;
+import static
org.apache.iotdb.db.conf.IoTDBConstant.FUNCTION_TYPE_EXTERNAL_UDTF;
import static
org.apache.iotdb.tsfile.common.constant.TsFileConstant.TSFILE_SUFFIX;
import java.io.File;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -83,6 +90,7 @@ import org.apache.iotdb.db.metadata.mnode.MNode;
import org.apache.iotdb.db.metadata.mnode.MeasurementMNode;
import org.apache.iotdb.db.metadata.mnode.StorageGroupMNode;
import org.apache.iotdb.db.monitor.StatMonitor;
+import org.apache.iotdb.db.qp.constant.SQLConstant;
import org.apache.iotdb.db.qp.logical.Operator.OperatorType;
import org.apache.iotdb.db.qp.logical.sys.AuthorOperator;
import org.apache.iotdb.db.qp.logical.sys.AuthorOperator.AuthorType;
@@ -690,28 +698,78 @@ public class PlanExecutor implements IPlanExecutor {
return listDataSet;
}
- private QueryDataSet processShowFunctions(ShowFunctionsPlan showPlan) {
+ private QueryDataSet processShowFunctions(ShowFunctionsPlan showPlan)
+ throws QueryProcessException {
ListDataSet listDataSet = new ListDataSet(
Arrays.asList(
new PartialPath(COLUMN_FUNCTION_NAME, false),
+ new PartialPath(COLUMN_FUNCTION_TYPE, false),
new PartialPath(COLUMN_FUNCTION_CLASS, false)
),
Arrays.asList(
TSDataType.TEXT,
+ TSDataType.TEXT,
TSDataType.TEXT
)
);
+
+ appendUDFs(listDataSet, showPlan);
+ appendNativeFunctions(listDataSet, showPlan);
+
+ listDataSet.sort((r1, r2) -> String.CASE_INSENSITIVE_ORDER
+ .compare(r1.getFields().get(0).getStringValue(),
r2.getFields().get(0).getStringValue()));
+ return listDataSet;
+ }
+
+ @SuppressWarnings("squid:S3776")
+ private void appendUDFs(ListDataSet listDataSet, ShowFunctionsPlan showPlan)
+ throws QueryProcessException {
for (UDFRegistrationInformation info : UDFRegistrationService.getInstance()
.getRegistrationInformation()) {
if (showPlan.showTemporary() && !info.isTemporary()) {
continue;
}
+
RowRecord rowRecord = new RowRecord(0); // ignore timestamp
rowRecord.addField(Binary.valueOf(info.getFunctionName()),
TSDataType.TEXT);
+ String functionType = "";
+ try {
+ if (info.isBuiltin()) {
+ if (info.isUDTF()) {
+ functionType = FUNCTION_TYPE_BUILTIN_UDTF;
+ } else if (info.isUDAF()) {
+ functionType = FUNCTION_TYPE_BUILTIN_UDAF;
+ }
+ } else {
+ if (info.isUDTF()) {
+ functionType = FUNCTION_TYPE_EXTERNAL_UDTF;
+ } else if (info.isUDAF()) {
+ functionType = FUNCTION_TYPE_EXTERNAL_UDAF;
+ }
+ }
+ } catch (InstantiationException | InvocationTargetException |
NoSuchMethodException | IllegalAccessException e) {
+ throw new QueryProcessException(e.toString());
+ }
+ rowRecord.addField(Binary.valueOf(functionType), TSDataType.TEXT);
rowRecord.addField(Binary.valueOf(info.getClassName()), TSDataType.TEXT);
listDataSet.putRecord(rowRecord);
}
- return listDataSet;
+ }
+
+ private void appendNativeFunctions(ListDataSet listDataSet,
ShowFunctionsPlan showPlan) {
+ if (showPlan.showTemporary()) {
+ return;
+ }
+
+ final Binary functionType = Binary.valueOf(FUNCTION_TYPE_NATIVE);
+ final Binary className = Binary.valueOf("");
+ for (String functionName : SQLConstant.getNativeFunctionNames()) {
+ RowRecord rowRecord = new RowRecord(0); // ignore timestamp
+ rowRecord.addField(Binary.valueOf(functionName), TSDataType.TEXT);
+ rowRecord.addField(functionType, TSDataType.TEXT);
+ rowRecord.addField(className, TSDataType.TEXT);
+ listDataSet.putRecord(rowRecord);
+ }
}
private void addRowRecordForShowQuery(
diff --git
a/server/src/main/java/org/apache/iotdb/db/query/dataset/ListDataSet.java
b/server/src/main/java/org/apache/iotdb/db/query/dataset/ListDataSet.java
index a312750..c8620ac 100644
--- a/server/src/main/java/org/apache/iotdb/db/query/dataset/ListDataSet.java
+++ b/server/src/main/java/org/apache/iotdb/db/query/dataset/ListDataSet.java
@@ -20,6 +20,7 @@
package org.apache.iotdb.db.query.dataset;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.List;
import org.apache.iotdb.db.metadata.PartialPath;
import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
@@ -28,11 +29,10 @@ import
org.apache.iotdb.tsfile.read.query.dataset.QueryDataSet;
public class ListDataSet extends QueryDataSet {
- private List<RowRecord> records = new ArrayList<>();
+ private final List<RowRecord> records = new ArrayList<>();
private int index = 0;
- public ListDataSet(List<PartialPath> paths,
- List<TSDataType> dataTypes) {
+ public ListDataSet(List<PartialPath> paths, List<TSDataType> dataTypes) {
super(new ArrayList<>(paths), dataTypes);
}
@@ -51,6 +51,10 @@ public class ListDataSet extends QueryDataSet {
}
public void sortByTime() {
- records.sort(((o1, o2) -> Long.compare(o2.getTimestamp(),
o1.getTimestamp())));
+ records.sort((o1, o2) -> Long.compare(o2.getTimestamp(),
o1.getTimestamp()));
+ }
+
+ public void sort(Comparator<RowRecord> c) {
+ records.sort(c);
}
}
diff --git
a/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationInformation.java
b/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/BuiltinFunction.java
similarity index 60%
copy from
server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationInformation.java
copy to
server/src/main/java/org/apache/iotdb/db/query/udf/builtin/BuiltinFunction.java
index c25bf69..c8bb083 100644
---
a/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationInformation.java
+++
b/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/BuiltinFunction.java
@@ -1,57 +1,43 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.iotdb.db.query.udf.service;
-
-public class UDFRegistrationInformation {
-
- private final String functionName;
- private final String className;
- private final boolean isTemporary;
-
- private Class<?> functionClass;
-
- public UDFRegistrationInformation(String functionName, String className,
boolean isTemporary,
- Class<?> functionClass) {
- this.functionName = functionName;
- this.className = className;
- this.isTemporary = isTemporary;
- this.functionClass = functionClass;
- }
-
- public String getFunctionName() {
- return functionName;
- }
-
- public String getClassName() {
- return className;
- }
-
- public boolean isTemporary() {
- return isTemporary;
- }
-
- public Class<?> getFunctionClass() {
- return functionClass;
- }
-
- public void updateFunctionClass(UDFClassLoader udfClassLoader) throws
ClassNotFoundException {
- functionClass = Class.forName(className, true, udfClassLoader);
- }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.query.udf.builtin;
+
+/**
+ * All built-in UDFs need to register function names and full class names here.
+ */
+public enum BuiltinFunction {
+ ;
+
+ private final String functionName;
+ private final String className;
+
+ BuiltinFunction(String functionName, String className) {
+ this.functionName = functionName;
+ this.className = className;
+ }
+
+ public String getFunctionName() {
+ return functionName;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+}
diff --git
a/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationInformation.java
b/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationInformation.java
index c25bf69..2c072a9 100644
---
a/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationInformation.java
+++
b/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationInformation.java
@@ -19,19 +19,24 @@
package org.apache.iotdb.db.query.udf.service;
+import java.lang.reflect.InvocationTargetException;
+import org.apache.iotdb.db.query.udf.api.UDTF;
+
public class UDFRegistrationInformation {
private final String functionName;
private final String className;
private final boolean isTemporary;
+ private final boolean isBuiltin;
private Class<?> functionClass;
public UDFRegistrationInformation(String functionName, String className,
boolean isTemporary,
- Class<?> functionClass) {
+ boolean isBuiltin, Class<?> functionClass) {
this.functionName = functionName;
this.className = className;
this.isTemporary = isTemporary;
+ this.isBuiltin = isBuiltin;
this.functionClass = functionClass;
}
@@ -43,8 +48,15 @@ public class UDFRegistrationInformation {
return className;
}
+ /**
+ * For a builtin function, this method always returns false.
+ */
public boolean isTemporary() {
- return isTemporary;
+ return !isBuiltin && isTemporary;
+ }
+
+ public boolean isBuiltin() {
+ return isBuiltin;
}
public Class<?> getFunctionClass() {
@@ -54,4 +66,13 @@ public class UDFRegistrationInformation {
public void updateFunctionClass(UDFClassLoader udfClassLoader) throws
ClassNotFoundException {
functionClass = Class.forName(className, true, udfClassLoader);
}
+
+ public boolean isUDTF()
+ throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException {
+ return functionClass.getDeclaredConstructor().newInstance() instanceof
UDTF;
+ }
+
+ public boolean isUDAF() {
+ return false;
+ }
}
diff --git
a/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationService.java
b/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationService.java
index 1ef46be..62e82ec 100644
---
a/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationService.java
+++
b/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationService.java
@@ -24,11 +24,8 @@ import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
-import java.util.Arrays;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Map.Entry;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -40,6 +37,7 @@ import org.apache.iotdb.db.exception.UDFRegistrationException;
import org.apache.iotdb.db.exception.query.QueryProcessException;
import org.apache.iotdb.db.qp.constant.SQLConstant;
import org.apache.iotdb.db.query.udf.api.UDF;
+import org.apache.iotdb.db.query.udf.builtin.BuiltinFunction;
import org.apache.iotdb.db.query.udf.core.context.UDFContext;
import org.apache.iotdb.db.service.IService;
import org.apache.iotdb.db.service.ServiceType;
@@ -58,11 +56,6 @@ public class UDFRegistrationService implements IService {
private static final String LOG_FILE_NAME = ULOG_FILE_DIR + "ulog.txt";
private static final String TEMPORARY_LOG_FILE_NAME = LOG_FILE_NAME + ".tmp";
- private static final Set<String> BUILTIN_FUNCTION_NAMES = new
HashSet<>(Arrays.asList(
- SQLConstant.MIN_TIME, SQLConstant.MAX_TIME, SQLConstant.MIN_VALUE,
SQLConstant.MAX_VALUE,
- SQLConstant.FIRST_VALUE, SQLConstant.LAST_VALUE, SQLConstant.COUNT,
SQLConstant.SUM,
- SQLConstant.AVG));
-
private final ReentrantLock registrationLock;
private final ConcurrentHashMap<String, UDFRegistrationInformation>
registrationInformation;
@@ -83,14 +76,41 @@ public class UDFRegistrationService implements IService {
registrationLock.unlock();
}
- @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity
warning
public void register(String functionName, String className, boolean
isTemporary,
boolean writeToTemporaryLogFile) throws UDFRegistrationException {
validateFunctionName(functionName, className);
+ checkIfRegistered(functionName, className, isTemporary);
+ doRegister(functionName, className, isTemporary);
+ tryAppendRegistrationLog(functionName, className, isTemporary,
writeToTemporaryLogFile);
+ }
+
+ private static void validateFunctionName(String functionName, String
className)
+ throws UDFRegistrationException {
+ if
(!SQLConstant.getNativeFunctionNames().contains(functionName.toLowerCase())) {
+ return;
+ }
+
+ String errorMessage = String.format(
+ "Failed to register UDF %s(%s), because the given function name
conflicts with the built-in function name",
+ functionName, className);
+ logger.warn(errorMessage);
+ throw new UDFRegistrationException(errorMessage);
+ }
+
+ private void checkIfRegistered(String functionName, String className,
boolean isTemporary)
+ throws UDFRegistrationException {
UDFRegistrationInformation information =
registrationInformation.get(functionName);
- if (information != null) {
- String errorMessage;
+ if (information == null) {
+ return;
+ }
+
+ String errorMessage;
+ if (information.isBuiltin()) {
+ errorMessage = String.format(
+ "Failed to register UDF %s(%s), because the given function name is
the same as a built-in UDF function name.",
+ functionName, className);
+ } else {
if (information.getClassName().equals(className)) {
errorMessage = String.format(
"Failed to register %sTEMPORARY UDF %s(%s), because a %sTEMPORARY
UDF %s(%s) with the same function name and the class name has already been
registered.",
@@ -103,10 +123,14 @@ public class UDFRegistrationService implements IService {
functionName, className,
information.getFunctionName(), information.getClassName());
}
- logger.warn(errorMessage);
- throw new UDFRegistrationException(errorMessage);
}
+ logger.warn(errorMessage);
+ throw new UDFRegistrationException(errorMessage);
+ }
+
+ private void doRegister(String functionName, String className, boolean
isTemporary)
+ throws UDFRegistrationException {
acquireRegistrationLock();
try {
UDFClassLoader currentActiveClassLoader =
UDFClassLoaderManager.getInstance()
@@ -116,7 +140,8 @@ public class UDFRegistrationService implements IService {
Class<?> functionClass = Class.forName(className, true,
currentActiveClassLoader);
functionClass.getDeclaredConstructor().newInstance();
registrationInformation.put(functionName,
- new UDFRegistrationInformation(functionName, className, isTemporary,
functionClass));
+ new UDFRegistrationInformation(functionName, className, isTemporary,
false,
+ functionClass));
} catch (IOException | InstantiationException | InvocationTargetException
| NoSuchMethodException | IllegalAccessException | ClassNotFoundException e) {
String errorMessage = String.format(
"Failed to register UDF %s(%s), because its instance can not be
constructed successfully. Exception: %s",
@@ -126,36 +151,32 @@ public class UDFRegistrationService implements IService {
} finally {
releaseRegistrationLock();
}
+ }
- if (writeToTemporaryLogFile && !isTemporary) {
- try {
- appendRegistrationLog(functionName, className);
- } catch (IOException e) {
- registrationInformation.remove(functionName);
- String errorMessage = String
- .format("Failed to append UDF log when registering UDF %s(%s),
because %s",
- functionName, className, e.toString());
- logger.error(errorMessage);
- throw new UDFRegistrationException(errorMessage, e);
- }
+ private void tryAppendRegistrationLog(String functionName, String className,
boolean isTemporary,
+ boolean writeToTemporaryLogFile) throws UDFRegistrationException {
+ if (!writeToTemporaryLogFile || isTemporary) {
+ return;
}
- }
- private static void validateFunctionName(String functionName, String
className)
- throws UDFRegistrationException {
- if (BUILTIN_FUNCTION_NAMES.contains(functionName.toLowerCase())) {
- String errorMessage = String.format(
- "Failed to register UDF %s(%s), because the given function name
conflicts with the built-in function name",
- functionName, className);
- logger.warn(errorMessage);
- throw new UDFRegistrationException(errorMessage);
+ try {
+ appendRegistrationLog(functionName, className);
+ } catch (IOException e) {
+ registrationInformation.remove(functionName);
+ String errorMessage = String
+ .format("Failed to append UDF log when registering UDF %s(%s),
because %s",
+ functionName, className, e.toString());
+ logger.error(errorMessage);
+ throw new UDFRegistrationException(errorMessage, e);
}
}
private void updateAllRegisteredClasses(UDFClassLoader activeClassLoader)
throws ClassNotFoundException {
for (UDFRegistrationInformation information :
getRegistrationInformation()) {
- information.updateFunctionClass(activeClassLoader);
+ if (!information.isBuiltin()) {
+ information.updateFunctionClass(activeClassLoader);
+ }
}
}
@@ -167,6 +188,13 @@ public class UDFRegistrationService implements IService {
throw new UDFRegistrationException(errorMessage);
}
+ if (information.isBuiltin()) {
+ String errorMessage = String
+ .format("Built-in function %s can not be deregistered.",
functionName);
+ logger.error(errorMessage);
+ throw new UDFRegistrationException(errorMessage);
+ }
+
if (!information.isTemporary()) {
try {
appendDeregistrationLog(functionName);
@@ -209,8 +237,11 @@ public class UDFRegistrationService implements IService {
throw new QueryProcessException(errorMessage);
}
- Thread.currentThread()
-
.setContextClassLoader(UDFClassLoaderManager.getInstance().getActiveClassLoader());
+ if (!information.isBuiltin()) {
+ Thread.currentThread()
+
.setContextClassLoader(UDFClassLoaderManager.getInstance().getActiveClassLoader());
+ }
+
try {
return (UDF)
information.getFunctionClass().getDeclaredConstructor().newInstance();
} catch (InstantiationException | InvocationTargetException |
NoSuchMethodException | IllegalAccessException e) {
@@ -228,6 +259,7 @@ public class UDFRegistrationService implements IService {
@Override
public void start() throws StartupException {
try {
+ registerBuiltinFunctions();
makeDirIfNecessary();
doRecovery();
logWriter = new UDFLogWriter(LOG_FILE_NAME);
@@ -236,6 +268,17 @@ public class UDFRegistrationService implements IService {
}
}
+ private void registerBuiltinFunctions() throws ClassNotFoundException {
+ ClassLoader classLoader = getClass().getClassLoader();
+ for (BuiltinFunction builtinFunction : BuiltinFunction.values()) {
+ String functionName = builtinFunction.getFunctionName();
+ String className = builtinFunction.getClassName();
+ Class<?> functionClass = Class.forName(className, true, classLoader);
+ registrationInformation.put(functionName,
+ new UDFRegistrationInformation(functionName, className, false, true,
functionClass));
+ }
+ }
+
private void makeDirIfNecessary() throws IOException {
File file = SystemFileFactory.INSTANCE.getFile(ULOG_FILE_DIR);
if (file.exists() && file.isDirectory()) {
@@ -305,7 +348,7 @@ public class UDFRegistrationService implements IService {
private void writeTemporaryLogFile() throws IOException {
UDFLogWriter temporaryLogFile = new UDFLogWriter(TEMPORARY_LOG_FILE_NAME);
for (UDFRegistrationInformation information :
registrationInformation.values()) {
- if (information.isTemporary()) {
+ if (information.isBuiltin() || information.isTemporary()) {
continue;
}
temporaryLogFile.register(information.getFunctionName(),
information.getClassName());
@@ -316,10 +359,26 @@ public class UDFRegistrationService implements IService {
@TestOnly
public void deregisterAll() throws UDFRegistrationException {
for (UDFRegistrationInformation information :
getRegistrationInformation()) {
- deregister(information.getFunctionName());
+ if (!information.isBuiltin()) {
+ deregister(information.getFunctionName());
+ }
}
}
+ @TestOnly
+ public void registerBuiltinFunction(String functionName, String className)
+ throws ClassNotFoundException {
+ ClassLoader classLoader = getClass().getClassLoader();
+ Class<?> functionClass = Class.forName(className, true, classLoader);
+ registrationInformation.put(functionName,
+ new UDFRegistrationInformation(functionName, className, false, true,
functionClass));
+ }
+
+ @TestOnly
+ public void deregisterBuiltinFunction(String functionName) {
+ registrationInformation.remove(functionName);
+ }
+
@Override
public ServiceType getID() {
return ServiceType.UDF_REGISTRATION_SERVICE;
diff --git
a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBUDFManagementIT.java
b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBUDFManagementIT.java
index 61cbd7d..14923bb 100644
---
a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBUDFManagementIT.java
+++
b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBUDFManagementIT.java
@@ -19,6 +19,9 @@
package org.apache.iotdb.db.integration;
+import static
org.apache.iotdb.db.conf.IoTDBConstant.FUNCTION_TYPE_BUILTIN_UDTF;
+import static
org.apache.iotdb.db.conf.IoTDBConstant.FUNCTION_TYPE_EXTERNAL_UDTF;
+import static org.apache.iotdb.db.conf.IoTDBConstant.FUNCTION_TYPE_NATIVE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -29,6 +32,8 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.iotdb.db.metadata.PartialPath;
+import org.apache.iotdb.db.qp.constant.SQLConstant;
+import org.apache.iotdb.db.query.udf.service.UDFRegistrationService;
import org.apache.iotdb.db.service.IoTDB;
import org.apache.iotdb.db.utils.EnvironmentUtils;
import org.apache.iotdb.jdbc.Config;
@@ -42,6 +47,8 @@ import org.junit.Test;
public class IoTDBUDFManagementIT {
+ private static final int NATIVE_FUNCTIONS_COUNT =
SQLConstant.getNativeFunctionNames().size();
+
@Before
public void setUp() throws Exception {
EnvironmentUtils.envSetUp();
@@ -67,26 +74,34 @@ public class IoTDBUDFManagementIT {
Statement statement = connection.createStatement()) {
statement.execute("create function udf as
\"org.apache.iotdb.db.query.udf.example.Adder\"");
statement.execute("select udf(*, *) from root.vehicle");
+
ResultSet resultSet = statement.executeQuery("show functions");
- assertEquals(2, resultSet.getMetaData().getColumnCount());
+ assertEquals(3, resultSet.getMetaData().getColumnCount());
int count = 0;
while (resultSet.next()) {
- ++count;
StringBuilder stringBuilder = new StringBuilder();
for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); ++i) {
stringBuilder.append(resultSet.getString(i)).append(",");
}
- Assert.assertEquals("udf,org.apache.iotdb.db.query.udf.example.Adder,",
- stringBuilder.toString());
+ String result = stringBuilder.toString();
+ if (result.contains(FUNCTION_TYPE_NATIVE)) {
+ continue;
+ }
+
+
Assert.assertEquals(String.format("udf,%s,org.apache.iotdb.db.query.udf.example.Adder,",
+ FUNCTION_TYPE_EXTERNAL_UDTF), result);
+ ++count;
}
Assert.assertEquals(1, count);
+
resultSet = statement.executeQuery("show temporary functions");
count = 0;
while (resultSet.next()) {
++count;
}
Assert.assertEquals(0, count);
- assertEquals(2, resultSet.getMetaData().getColumnCount());
+ assertEquals(3, resultSet.getMetaData().getColumnCount());
+
statement.execute("drop function udf");
} catch (SQLException throwable) {
throwable.printStackTrace();
@@ -101,57 +116,64 @@ public class IoTDBUDFManagementIT {
Statement statement = connection.createStatement()) {
statement.execute("create function udf as
\"org.apache.iotdb.db.query.udf.example.Adder\"");
statement.execute("select udf(*, *) from root.vehicle");
+
ResultSet resultSet = statement.executeQuery("show functions");
int count = 0;
while (resultSet.next()) {
++count;
}
- Assert.assertEquals(1, count);
- assertEquals(2, resultSet.getMetaData().getColumnCount());
+ Assert.assertEquals(1 + NATIVE_FUNCTIONS_COUNT, count);
+ assertEquals(3, resultSet.getMetaData().getColumnCount());
+
resultSet = statement.executeQuery("show temporary functions");
count = 0;
while (resultSet.next()) {
++count;
}
Assert.assertEquals(0, count);
- assertEquals(2, resultSet.getMetaData().getColumnCount());
- statement.execute("drop function udf");
+ assertEquals(3, resultSet.getMetaData().getColumnCount());
+ statement.execute("drop function udf");
statement.execute(
"create temporary function udf as
\"org.apache.iotdb.db.query.udf.example.Adder\"");
statement.execute("select udf(*, *) from root.vehicle");
+
resultSet = statement.executeQuery("show functions");
count = 0;
while (resultSet.next()) {
++count;
}
- Assert.assertEquals(1, count);
- assertEquals(2, resultSet.getMetaData().getColumnCount());
+ Assert.assertEquals(1 + NATIVE_FUNCTIONS_COUNT, count);
+ assertEquals(3, resultSet.getMetaData().getColumnCount());
+
resultSet = statement.executeQuery("show temporary functions");
count = 0;
while (resultSet.next()) {
++count;
}
Assert.assertEquals(1, count);
- assertEquals(2, resultSet.getMetaData().getColumnCount());
- statement.execute("drop function udf");
+ assertEquals(3, resultSet.getMetaData().getColumnCount());
+ statement.execute("drop function udf");
statement.execute("create function udf as
\"org.apache.iotdb.db.query.udf.example.Adder\"");
statement.execute("select udf(*, *) from root.vehicle");
+
resultSet = statement.executeQuery("show functions");
count = 0;
while (resultSet.next()) {
++count;
}
- Assert.assertEquals(1, count);
- assertEquals(2, resultSet.getMetaData().getColumnCount());
+ Assert.assertEquals(1 + NATIVE_FUNCTIONS_COUNT, count);
+ assertEquals(3, resultSet.getMetaData().getColumnCount());
+
resultSet = statement.executeQuery("show temporary functions");
count = 0;
while (resultSet.next()) {
++count;
}
Assert.assertEquals(0, count);
- assertEquals(2, resultSet.getMetaData().getColumnCount());
+ assertEquals(3, resultSet.getMetaData().getColumnCount());
+
statement.execute("drop function udf");
} catch (SQLException throwable) {
throwable.printStackTrace();
@@ -218,6 +240,7 @@ public class IoTDBUDFManagementIT {
.getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
Statement statement = connection.createStatement()) {
statement.execute("create function udf as
\"org.apache.iotdb.db.query.udf.example.Adder\"");
+
try {
statement.execute("create function udf as
\"org.apache.iotdb.db.query.udf.example.Adder\"");
fail();
@@ -234,6 +257,7 @@ public class IoTDBUDFManagementIT {
Statement statement = connection.createStatement()) {
statement.execute(
"create temporary function udf as
\"org.apache.iotdb.db.query.udf.example.Adder\"");
+
try {
statement.execute(
"create temporary function udf as
\"org.apache.iotdb.db.query.udf.example.Adder\"");
@@ -251,6 +275,7 @@ public class IoTDBUDFManagementIT {
Statement statement = connection.createStatement()) {
statement.execute(
"create temporary function udf as
\"org.apache.iotdb.db.query.udf.example.Adder\"");
+
try {
statement.execute("create function udf as
\"org.apache.iotdb.db.query.udf.example.Adder\"");
fail();
@@ -267,6 +292,7 @@ public class IoTDBUDFManagementIT {
Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root");
Statement statement = connection.createStatement()) {
statement.execute("create function udf as
\"org.apache.iotdb.db.query.udf.example.Adder\"");
+
try {
statement.execute(
"create temporary function udf as
\"org.apache.iotdb.db.query.udf.example.Adder\"");
@@ -285,6 +311,7 @@ public class IoTDBUDFManagementIT {
Statement statement = connection.createStatement()) {
statement.execute("create function udf as
\"org.apache.iotdb.db.query.udf.example.Adder\"");
statement.execute("drop function udf");
+
try {
statement.execute("drop function udf");
fail();
@@ -305,4 +332,107 @@ public class IoTDBUDFManagementIT {
assertTrue(throwable.getMessage().contains("does not exist"));
}
}
+
+ @Test
+ public void testCreateBuiltinFunction() throws ClassNotFoundException {
+ UDFRegistrationService.getInstance().registerBuiltinFunction("adder",
+ "org.apache.iotdb.db.query.udf.example.Adder");
+ try (Connection connection = DriverManager.getConnection(
+ Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root");
+ Statement statement = connection.createStatement()) {
+ statement.execute("create function adder as
\"org.apache.iotdb.db.query.udf.example.Adder\"");
+ fail();
+ } catch (SQLException throwable) {
+ throwable.printStackTrace();
+ assertTrue(throwable.getMessage()
+ .contains("the given function name is the same as a built-in UDF
function name"));
+ } finally {
+ UDFRegistrationService.getInstance().deregisterBuiltinFunction("adder");
+ }
+ }
+
+ @Test
+ public void testDropBuiltinFunction() throws ClassNotFoundException {
+ UDFRegistrationService.getInstance().registerBuiltinFunction("adder",
+ "org.apache.iotdb.db.query.udf.example.Adder");
+ try (Connection connection = DriverManager.getConnection(
+ Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root");
+ Statement statement = connection.createStatement()) {
+ statement.execute("drop function adder");
+ fail();
+ } catch (SQLException throwable) {
+ throwable.printStackTrace();
+ assertTrue(
+ throwable.getMessage().contains("Built-in function adder can not be
deregistered"));
+ } finally {
+ UDFRegistrationService.getInstance().deregisterBuiltinFunction("adder");
+ }
+ }
+
+ @Test
+ public void testReflectBuiltinFunction() throws ClassNotFoundException {
+ UDFRegistrationService.getInstance().registerBuiltinFunction("adder",
+ "org.apache.iotdb.db.query.udf.example.Adder");
+ try (Connection connection = DriverManager.getConnection(
+ Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root");
+ Statement statement = connection.createStatement()) {
+ statement.execute("select adder(*, *) from root.vehicle");
+ } catch (SQLException throwable) {
+ throwable.printStackTrace();
+ fail(throwable.getMessage());
+ } finally {
+ UDFRegistrationService.getInstance().deregisterBuiltinFunction("adder");
+ }
+ }
+
+ @Test
+ public void testShowBuiltinFunction() throws ClassNotFoundException {
+ UDFRegistrationService.getInstance().registerBuiltinFunction("adder",
+ "org.apache.iotdb.db.query.udf.example.Adder");
+ try (Connection connection = DriverManager.getConnection(
+ Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root");
+ Statement statement = connection.createStatement()) {
+ statement.execute("create function udf as
\"org.apache.iotdb.db.query.udf.example.Adder\"");
+
+ ResultSet resultSet = statement.executeQuery("show functions");
+ assertEquals(3, resultSet.getMetaData().getColumnCount());
+ int count = 0;
+ while (resultSet.next()) {
+ StringBuilder stringBuilder = new StringBuilder();
+ for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); ++i) {
+ stringBuilder.append(resultSet.getString(i)).append(",");
+ }
+ String result = stringBuilder.toString();
+ if (result.contains(FUNCTION_TYPE_NATIVE)) {
+ continue;
+ }
+
+ if (result.contains(FUNCTION_TYPE_EXTERNAL_UDTF)) {
+
Assert.assertEquals(String.format("udf,%s,org.apache.iotdb.db.query.udf.example.Adder,",
+ FUNCTION_TYPE_EXTERNAL_UDTF), result);
+ ++count;
+ } else if (result.contains(FUNCTION_TYPE_BUILTIN_UDTF)) {
+
Assert.assertEquals(String.format("adder,%s,org.apache.iotdb.db.query.udf.example.Adder,",
+ FUNCTION_TYPE_BUILTIN_UDTF), result);
+ ++count;
+ }
+ }
+ Assert.assertEquals(2, count);
+
+ resultSet = statement.executeQuery("show temporary functions");
+ count = 0;
+ while (resultSet.next()) {
+ ++count;
+ }
+ Assert.assertEquals(0, count);
+ assertEquals(3, resultSet.getMetaData().getColumnCount());
+
+ statement.execute("drop function udf");
+ } catch (SQLException throwable) {
+ throwable.printStackTrace();
+ fail(throwable.getMessage());
+ } finally {
+ UDFRegistrationService.getInstance().deregisterBuiltinFunction("adder");
+ }
+ }
}