You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flink.apache.org by bl...@apache.org on 2019/06/19 20:39:20 UTC

[flink] branch master updated: [FLINK-12658][hive] Integrate Flink with Hive GenericUDF

This is an automated email from the ASF dual-hosted git repository.

bli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git


The following commit(s) were added to refs/heads/master by this push:
     new 6f89f3d  [FLINK-12658][hive] Integrate Flink with Hive GenericUDF
6f89f3d is described below

commit 6f89f3d0720823019e3200a4eb572f7281657344
Author: bowen.li <bo...@gmail.com>
AuthorDate: Tue Jun 18 13:16:20 2019 -0700

    [FLINK-12658][hive] Integrate Flink with Hive GenericUDF
    
    This PR integrates Flink with Hive GenericUDF.
    
    This closes #8770.
---
 .../functions/hive/DeferredObjectAdapter.java      |  54 ++++
 .../flink/table/functions/hive/HiveGenericUDF.java | 105 ++++++++
 .../functions/hive/conversion/HiveInspectors.java  |  84 ++++++
 .../table/functions/hive/HiveGenericUDFTest.java   | 291 +++++++++++++++++++++
 .../table/functions/hive/HiveSimpleUDFTest.java    |   2 +-
 5 files changed, 535 insertions(+), 1 deletion(-)

diff --git a/flink-connectors/flink-connector-hive/src/main/java/org/apache/flink/table/functions/hive/DeferredObjectAdapter.java b/flink-connectors/flink-connector-hive/src/main/java/org/apache/flink/table/functions/hive/DeferredObjectAdapter.java
new file mode 100644
index 0000000..c008573
--- /dev/null
+++ b/flink-connectors/flink-connector-hive/src/main/java/org/apache/flink/table/functions/hive/DeferredObjectAdapter.java
@@ -0,0 +1,54 @@
+/*
+ * 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.flink.table.functions.hive;
+
+import org.apache.flink.annotation.Internal;
+import org.apache.flink.table.functions.hive.conversion.HiveInspectors;
+import org.apache.flink.table.functions.hive.conversion.HiveObjectConversion;
+import org.apache.flink.table.types.DataType;
+
+import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
+import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
+
+/**
+ * Adapter from Java Object to Hive DeferredObject.
+ */
+@Internal
+public class DeferredObjectAdapter implements GenericUDF.DeferredObject {
+
+	private Object object;
+	private HiveObjectConversion conversion;
+
+	public DeferredObjectAdapter(ObjectInspector inspector, DataType dataType) {
+		conversion = HiveInspectors.getConversion(inspector, dataType);
+	}
+
+	public void set(Object ob) {
+		this.object = ob;
+	}
+
+	@Override
+	public void prepare(int version) {
+	}
+
+	@Override
+	public Object get() {
+		return conversion.toHiveObject(object);
+	}
+}
diff --git a/flink-connectors/flink-connector-hive/src/main/java/org/apache/flink/table/functions/hive/HiveGenericUDF.java b/flink-connectors/flink-connector-hive/src/main/java/org/apache/flink/table/functions/hive/HiveGenericUDF.java
new file mode 100644
index 0000000..fde0fd4
--- /dev/null
+++ b/flink-connectors/flink-connector-hive/src/main/java/org/apache/flink/table/functions/hive/HiveGenericUDF.java
@@ -0,0 +1,105 @@
+/*
+ * 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.flink.table.functions.hive;
+
+import org.apache.flink.annotation.Internal;
+import org.apache.flink.table.catalog.hive.util.HiveTypeUtil;
+import org.apache.flink.table.functions.hive.conversion.HiveInspectors;
+import org.apache.flink.table.types.DataType;
+
+import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
+import org.apache.hadoop.hive.ql.metadata.HiveException;
+import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
+import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
+import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A ScalarFunction implementation that calls Hive's {@link GenericUDF}.
+ */
+@Internal
+public class HiveGenericUDF extends HiveScalarFunction<GenericUDF> {
+
+	private static final Logger LOG = LoggerFactory.getLogger(HiveGenericUDF.class);
+
+	private transient GenericUDF.DeferredObject[] deferredObjects;
+
+	public HiveGenericUDF(HiveFunctionWrapper<GenericUDF> hiveFunctionWrapper) {
+		super(hiveFunctionWrapper);
+
+		LOG.info("Creating HiveGenericUDF from '{}'", hiveFunctionWrapper.getClassName());
+	}
+
+	@Override
+	public void openInternal() {
+
+		LOG.info("Open HiveGenericUDF as {}", hiveFunctionWrapper.getClassName());
+
+		function = hiveFunctionWrapper.createFunction();
+
+		try {
+			returnInspector = function.initializeAndFoldConstants(
+				HiveInspectors.toInspectors(constantArguments, argTypes));
+		} catch (UDFArgumentException e) {
+			throw new FlinkHiveUDFException(e);
+		}
+
+		deferredObjects = new GenericUDF.DeferredObject[argTypes.length];
+
+		for (int i = 0; i < deferredObjects.length; i++) {
+			deferredObjects[i] = new DeferredObjectAdapter(
+				TypeInfoUtils.getStandardJavaObjectInspectorFromTypeInfo(
+					HiveTypeUtil.toHiveTypeInfo(argTypes[i])),
+				argTypes[i]
+			);
+		}
+	}
+
+	@Override
+	public Object evalInternal(Object[] args) {
+
+		for (int i = 0; i < args.length; i++) {
+			((DeferredObjectAdapter) deferredObjects[i]).set(args[i]);
+		}
+
+		try {
+			return HiveInspectors.toFlinkObject(returnInspector, function.evaluate(deferredObjects));
+		} catch (HiveException e) {
+			throw new FlinkHiveUDFException(e);
+		}
+	}
+
+	@Override
+	public DataType getHiveResultType(Object[] constantArguments, DataType[] argTypes) {
+		LOG.info("Getting result type of HiveGenericUDF from {}", hiveFunctionWrapper.getClassName());
+
+		try {
+			ObjectInspector[] argumentInspectors = HiveInspectors.toInspectors(constantArguments, argTypes);
+
+			ObjectInspector resultObjectInspector =
+				hiveFunctionWrapper.createFunction().initializeAndFoldConstants(argumentInspectors);
+
+			return HiveTypeUtil.toFlinkType(
+				TypeInfoUtils.getTypeInfoFromObjectInspector(resultObjectInspector));
+		} catch (UDFArgumentException e) {
+			throw new FlinkHiveUDFException(e);
+		}
+	}
+}
diff --git a/flink-connectors/flink-connector-hive/src/main/java/org/apache/flink/table/functions/hive/conversion/HiveInspectors.java b/flink-connectors/flink-connector-hive/src/main/java/org/apache/flink/table/functions/hive/conversion/HiveInspectors.java
index e9bae47..37a50b4 100644
--- a/flink-connectors/flink-connector-hive/src/main/java/org/apache/flink/table/functions/hive/conversion/HiveInspectors.java
+++ b/flink-connectors/flink-connector-hive/src/main/java/org/apache/flink/table/functions/hive/conversion/HiveInspectors.java
@@ -26,6 +26,7 @@ import org.apache.flink.table.types.logical.CharType;
 import org.apache.flink.table.types.logical.VarCharType;
 
 import org.apache.hadoop.hive.common.type.HiveChar;
+import org.apache.hadoop.hive.common.type.HiveDecimal;
 import org.apache.hadoop.hive.common.type.HiveVarchar;
 import org.apache.hadoop.hive.serde2.io.ByteWritable;
 import org.apache.hadoop.hive.serde2.io.DateWritable;
@@ -34,6 +35,7 @@ import org.apache.hadoop.hive.serde2.io.HiveCharWritable;
 import org.apache.hadoop.hive.serde2.io.HiveVarcharWritable;
 import org.apache.hadoop.hive.serde2.io.ShortWritable;
 import org.apache.hadoop.hive.serde2.io.TimestampWritable;
+import org.apache.hadoop.hive.serde2.objectinspector.ConstantObjectInspector;
 import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
 import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;
 import org.apache.hadoop.hive.serde2.objectinspector.primitive.BinaryObjectInspector;
@@ -48,6 +50,20 @@ import org.apache.hadoop.hive.serde2.objectinspector.primitive.IntObjectInspecto
 import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaBinaryObjectInspector;
 import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaBooleanObjectInspector;
 import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaByteObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaConstantBinaryObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaConstantBooleanObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaConstantByteObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaConstantDateObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaConstantDoubleObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaConstantFloatObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaConstantHiveCharObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaConstantHiveDecimalObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaConstantHiveVarcharObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaConstantIntObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaConstantLongObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaConstantShortObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaConstantStringObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaConstantTimestampObjectInspector;
 import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaDateObjectInspector;
 import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaDoubleObjectInspector;
 import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaFloatObjectInspector;
@@ -89,6 +105,74 @@ import java.sql.Timestamp;
 public class HiveInspectors {
 
 	/**
+	 * Get an array of ObjectInspector from the give array of args and their types.
+	 */
+	public static ObjectInspector[] toInspectors(Object[] args, DataType[] argTypes) {
+		assert args.length == argTypes.length;
+
+		ObjectInspector[] argumentInspectors = new ObjectInspector[argTypes.length];
+
+		for (int i = 0; i < argTypes.length; i++) {
+			Object constant = args[i];
+
+			if (constant == null) {
+				argumentInspectors[i] =
+					TypeInfoUtils.getStandardJavaObjectInspectorFromTypeInfo(
+						HiveTypeUtil.toHiveTypeInfo(argTypes[i]));
+			} else {
+				argumentInspectors[i] =
+					HiveInspectors.getPrimitiveJavaConstantObjectInspector(
+						(PrimitiveTypeInfo) HiveTypeUtil.toHiveTypeInfo(argTypes[i]),
+						constant
+					);
+			}
+		}
+
+		return argumentInspectors;
+	}
+
+	private static ConstantObjectInspector getPrimitiveJavaConstantObjectInspector(PrimitiveTypeInfo typeInfo, Object value) {
+		switch (typeInfo.getPrimitiveCategory()) {
+			case BOOLEAN:
+				return new JavaConstantBooleanObjectInspector((Boolean) value);
+			case BYTE:
+				return new JavaConstantByteObjectInspector((Byte) value);
+			case SHORT:
+				return new JavaConstantShortObjectInspector((Short) value);
+			case INT:
+				return new JavaConstantIntObjectInspector((Integer) value);
+			case LONG:
+				return new JavaConstantLongObjectInspector((Long) value);
+			case FLOAT:
+				return new JavaConstantFloatObjectInspector((Float) value);
+			case DOUBLE:
+				return new JavaConstantDoubleObjectInspector((Double) value);
+			case STRING:
+				return new JavaConstantStringObjectInspector((String) value);
+			case CHAR:
+				return new JavaConstantHiveCharObjectInspector((HiveChar) value);
+			case VARCHAR:
+				return new JavaConstantHiveVarcharObjectInspector((HiveVarchar) value);
+			case DATE:
+				return new JavaConstantDateObjectInspector((Date) value);
+			case TIMESTAMP:
+				return new JavaConstantTimestampObjectInspector((Timestamp) value);
+			case DECIMAL:
+				// TODO: Needs more testing
+				return new JavaConstantHiveDecimalObjectInspector((HiveDecimal) value);
+			case BINARY:
+				return new JavaConstantBinaryObjectInspector((byte[]) value);
+			case UNKNOWN:
+			case VOID:
+				// If type is null, we use the Java Constant String to replace
+				return new JavaConstantStringObjectInspector((String) value);
+			default:
+				throw new FlinkHiveUDFException(
+					String.format("Cannot find ConstantObjectInspector for %s", typeInfo));
+		}
+	}
+
+	/**
 	 * Get conversion for converting Flink object to Hive object from an ObjectInspector and the corresponding Flink DataType.
 	 */
 	public static HiveObjectConversion getConversion(ObjectInspector inspector, DataType dataType) {
diff --git a/flink-connectors/flink-connector-hive/src/test/java/org/apache/flink/table/functions/hive/HiveGenericUDFTest.java b/flink-connectors/flink-connector-hive/src/test/java/org/apache/flink/table/functions/hive/HiveGenericUDFTest.java
new file mode 100644
index 0000000..1fb8a10
--- /dev/null
+++ b/flink-connectors/flink-connector-hive/src/test/java/org/apache/flink/table/functions/hive/HiveGenericUDFTest.java
@@ -0,0 +1,291 @@
+/*
+ * 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.flink.table.functions.hive;
+
+import org.apache.flink.table.api.DataTypes;
+import org.apache.flink.table.types.DataType;
+
+import org.apache.hadoop.hive.ql.udf.UDFUnhex;
+import org.apache.hadoop.hive.ql.udf.generic.GenericUDFAbs;
+import org.apache.hadoop.hive.ql.udf.generic.GenericUDFAddMonths;
+import org.apache.hadoop.hive.ql.udf.generic.GenericUDFCase;
+import org.apache.hadoop.hive.ql.udf.generic.GenericUDFCeil;
+import org.apache.hadoop.hive.ql.udf.generic.GenericUDFCoalesce;
+import org.apache.hadoop.hive.ql.udf.generic.GenericUDFDateDiff;
+import org.apache.hadoop.hive.ql.udf.generic.GenericUDFDateFormat;
+import org.apache.hadoop.hive.ql.udf.generic.GenericUDFDecode;
+import org.junit.Test;
+
+import java.sql.Date;
+import java.sql.Timestamp;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test for {@link HiveGenericUDF}.
+ */
+public class HiveGenericUDFTest {
+
+	@Test
+	public void testAbs() {
+		HiveGenericUDF udf = init(
+			GenericUDFAbs.class,
+			new Object[] {
+				null
+			},
+			new DataType[] {
+				DataTypes.DOUBLE()
+			}
+		);
+
+		assertEquals(10.0d, udf.eval(-10.0d));
+
+		udf = init(
+			GenericUDFAbs.class,
+			new Object[] {
+				null
+			},
+			new DataType[] {
+				DataTypes.INT()
+			}
+		);
+
+		assertEquals(10, udf.eval(-10));
+
+		udf = init(
+			GenericUDFAbs.class,
+			new Object[] {
+				null
+			},
+			new DataType[] {
+				DataTypes.STRING()
+			}
+		);
+
+		assertEquals(10.0, udf.eval("-10.0"));
+	}
+
+	@Test
+	public void testAddMonths() {
+		HiveGenericUDF udf = init(
+			GenericUDFAddMonths.class,
+			new Object[] {
+				null,
+				1
+			},
+			new DataType[] {
+				DataTypes.STRING(),
+				DataTypes.INT()
+			}
+		);
+
+		assertEquals("2009-09-30", udf.eval("2009-08-31", 1));
+		assertEquals("2009-09-30", udf.eval("2009-08-31 11:11:11", 1));
+	}
+
+	@Test
+	public void testDateFormat() {
+		String constYear = "y";
+		String constMonth = "M";
+
+		HiveGenericUDF udf = init(
+			GenericUDFDateFormat.class,
+			new Object[] {
+				null,
+				constYear
+			},
+			new DataType[] {
+				DataTypes.STRING(),
+				DataTypes.STRING()
+			}
+		);
+
+		assertEquals("2009", udf.eval("2009-08-31", constYear));
+
+		udf = init(
+			GenericUDFDateFormat.class,
+			new Object[] {
+				null,
+				constMonth
+			},
+			new DataType[] {
+				DataTypes.DATE(),
+				DataTypes.STRING()
+			}
+		);
+
+		assertEquals("8", udf.eval(Date.valueOf("2019-08-31"), constMonth));
+	}
+
+	@Test
+	public void testDecode() {
+		String constDecoding = "UTF-8";
+
+		HiveGenericUDF udf = init(
+			GenericUDFDecode.class,
+			new Object[] {
+				null,
+				constDecoding
+			},
+			new DataType[] {
+				DataTypes.BYTES(),
+				DataTypes.STRING()
+			}
+		);
+
+		HiveSimpleUDF simpleUDF = HiveSimpleUDFTest.init(
+			UDFUnhex.class,
+			new DataType[]{
+				DataTypes.STRING()
+			});
+
+		assertEquals("MySQL", udf.eval(simpleUDF.eval("4D7953514C"), constDecoding));
+	}
+
+	@Test
+	public void testCase() {
+		HiveGenericUDF udf = init(
+			GenericUDFCase.class,
+			new Object[] {
+				null,
+				"1",
+				"a",
+				"b"
+			},
+			new DataType[] {
+				DataTypes.STRING(),
+				DataTypes.STRING(),
+				DataTypes.STRING(),
+				DataTypes.STRING()
+			}
+		);
+
+		assertEquals("a", udf.eval("1", "1", "a", "b"));
+		assertEquals("b", udf.eval("2", "1", "a", "b"));
+	}
+
+	@Test
+	public void testCeil() {
+		HiveGenericUDF udf = init(
+			GenericUDFCeil.class,
+			new Object[] {
+				null
+			},
+			new DataType[] {
+				DataTypes.DOUBLE()
+			}
+		);
+
+		assertEquals(0L, udf.eval(-0.1d));
+
+		// TODO: reenable the test when we support decimal for Hive functions
+//		udf = init(
+//			GenericUDFCeil.class,
+//			new Object[] {
+//				null
+//			},
+//			new DataType[] {
+//				DataTypes.DECIMAL(1, 1)
+//			}
+//		);
+//
+//		assertEquals(0L, udf.eval(BigDecimal.valueOf(-0.1)));
+	}
+
+	@Test
+	public void testCoalesce() {
+		HiveGenericUDF udf = init(
+			GenericUDFCoalesce.class,
+			new Object[] {
+				null,
+				1,
+				null
+			},
+			new DataType[] {
+				DataTypes.INT(),
+				DataTypes.INT(),
+				DataTypes.INT()
+			}
+		);
+
+		assertEquals(1, udf.eval(null, 1, null));
+	}
+
+	@Test
+	public void testDataDiff() {
+
+		String d = "1969-07-20";
+		String t1 = "1969-07-20 00:00:00";
+		String t2 = "1980-12-31 12:59:59";
+
+		HiveGenericUDF udf = init(
+			GenericUDFDateDiff.class,
+			new Object[] {
+				null,
+				null
+			},
+			new DataType[] {
+				DataTypes.VARCHAR(20),
+				DataTypes.CHAR(20),
+			}
+		);
+
+		assertEquals(-4182, udf.eval(t1, t2));
+
+		udf = init(
+			GenericUDFDateDiff.class,
+			new Object[] {
+				null,
+				null
+			},
+			new DataType[] {
+				DataTypes.DATE(),
+				DataTypes.TIMESTAMP(),
+			}
+		);
+
+		assertEquals(-4182, udf.eval(Date.valueOf(d), Timestamp.valueOf(t2)));
+
+		// Test invalid char length
+		udf = init(
+			GenericUDFDateDiff.class,
+			new Object[] {
+				null,
+				null
+			},
+			new DataType[] {
+				DataTypes.CHAR(2),
+				DataTypes.VARCHAR(2),
+			}
+		);
+
+		assertEquals(null, udf.eval(t1, t2));
+	}
+
+	private static HiveGenericUDF init(Class hiveUdfClass, Object[] constantArgs, DataType[] argTypes) {
+		HiveGenericUDF udf = new HiveGenericUDF(new HiveFunctionWrapper(hiveUdfClass.getName()));
+
+		udf.setArgumentTypesAndConstants(constantArgs, argTypes);
+		udf.getHiveResultType(constantArgs, argTypes);
+
+		udf.open(null);
+
+		return udf;
+	}
+}
diff --git a/flink-connectors/flink-connector-hive/src/test/java/org/apache/flink/table/functions/hive/HiveSimpleUDFTest.java b/flink-connectors/flink-connector-hive/src/test/java/org/apache/flink/table/functions/hive/HiveSimpleUDFTest.java
index afb3341..81af06b 100644
--- a/flink-connectors/flink-connector-hive/src/test/java/org/apache/flink/table/functions/hive/HiveSimpleUDFTest.java
+++ b/flink-connectors/flink-connector-hive/src/test/java/org/apache/flink/table/functions/hive/HiveSimpleUDFTest.java
@@ -180,7 +180,7 @@ public class HiveSimpleUDFTest {
 		assertEquals("MySQL", new String((byte[]) udf.eval("4D7953514C"), "UTF-8"));
 	}
 
-	private HiveSimpleUDF init(Class hiveUdfClass, DataType[] argTypes) {
+	protected static HiveSimpleUDF init(Class hiveUdfClass, DataType[] argTypes) {
 		HiveSimpleUDF udf = new HiveSimpleUDF(new HiveFunctionWrapper(hiveUdfClass.getName()));
 
 		// Hive UDF won't have literal args