Index: ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionRegistry.java =================================================================== --- ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionRegistry.java (revision 1483152) +++ ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionRegistry.java (working copy) @@ -423,6 +423,7 @@ registerGenericUDTF("json_tuple", GenericUDTFJSONTuple.class); registerGenericUDTF("parse_url_tuple", GenericUDTFParseUrlTuple.class); registerGenericUDTF("stack", GenericUDTFStack.class); + registerGenericUDTF("left_explode", GenericUDTFLeftExplode.class); //PTF declarations registerGenericUDF(true, LEAD_FUNC_NAME, GenericUDFLead.class); Index: ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDTFLeftExplode.java =================================================================== --- ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDTFLeftExplode.java (revision 0) +++ ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDTFLeftExplode.java (working copy) @@ -0,0 +1,133 @@ +/** + * 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.hadoop.hive.ql.udf.generic; + +import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.hadoop.hive.ql.exec.Description; +import org.apache.hadoop.hive.ql.exec.TaskExecutionException; +import org.apache.hadoop.hive.ql.exec.UDFArgumentException; +import org.apache.hadoop.hive.ql.metadata.HiveException; +import org.apache.hadoop.hive.serde2.objectinspector.ListObjectInspector; +import org.apache.hadoop.hive.serde2.objectinspector.MapObjectInspector; +import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; +import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory; +import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector; +import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector; + +/** + * GenericUDTFExplode. + * + */ +@Description(name = "explode", value = "_FUNC_(a) - separates the elements of array a into multiple rows," + + " or the elements of a map into multiple rows and columns. However if a " + + "list is null a single null result is returned ") +public class GenericUDTFLeftExplode extends GenericUDTF { + + private ObjectInspector inputOI = null; + + @Override + public void close() throws HiveException { + } + + @Override + public StructObjectInspector initialize(ObjectInspector[] args) throws UDFArgumentException { + if (args.length != 1) { + throw new UDFArgumentException("Expected one argument was "+ args.length); + } + ArrayList fieldNames = new ArrayList(); + ArrayList fieldOIs = new ArrayList(); + switch (args[0].getCategory()) { + case PRIMITIVE: + inputOI = args[0]; + fieldNames.add("col"); + fieldOIs.add((PrimitiveObjectInspector) inputOI); + break; + case LIST: + inputOI = args[0]; + fieldNames.add("col"); + fieldOIs.add(((ListObjectInspector) inputOI).getListElementObjectInspector()); + break; + case MAP: + inputOI = args[0]; + fieldNames.add("key"); + fieldNames.add("value"); + fieldOIs.add(((MapObjectInspector) inputOI).getMapKeyObjectInspector()); + fieldOIs.add(((MapObjectInspector) inputOI).getMapValueObjectInspector()); + break; + default: + throw new UDFArgumentException("left_explode() takes an array or a map as a parameter"); + } + return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs); + } + + private final Object[] forwardListObj = new Object[1]; + private final Object[] forwardMapObj = new Object[2]; + + @Override + public void process(Object[] o) throws HiveException { + switch (inputOI.getCategory()) { + case PRIMITIVE: + // PrimitiveObjectInspector pOI = (PrimitiveObjectInspector) + forward(o); + break; + case LIST: + ListObjectInspector listOI = (ListObjectInspector) inputOI; + List list = listOI.getList(o[0]); + if (list == null) { + forward(forwardListObj); + return; + } + if (list.size() == 0) { + forward(forwardListObj); + return; + } + for (Object r : list) { + forwardListObj[0] = r; + forward(forwardListObj); + } + break; + case MAP: + MapObjectInspector mapOI = (MapObjectInspector) inputOI; + Map map = mapOI.getMap(o[0]); + if (map == null) { + forward(forwardMapObj); + return; + } + for (Entry r : map.entrySet()) { + forwardMapObj[0] = r.getKey(); + forwardMapObj[1] = r.getValue(); + forward(forwardMapObj); + } + break; + default: + throw new TaskExecutionException("left_explode() can only operate on an array or a map"); + } + } + + @Override + public String toString() { + return "left_explode"; + } +} Index: ql/src/test/queries/clientpositive/lexplode.q =================================================================== --- ql/src/test/queries/clientpositive/lexplode.q (revision 0) +++ ql/src/test/queries/clientpositive/lexplode.q (working copy) @@ -0,0 +1,4 @@ +SELECT left_explode(col) AS myCol FROM + (SELECT array(1,2,3) AS col FROM src LIMIT 1 ) a; + +SELECT left_explode(null) AS col FROM src LIMIT 1; Index: ql/src/test/results/clientpositive/lexplode.q.out =================================================================== --- ql/src/test/results/clientpositive/lexplode.q.out (revision 0) +++ ql/src/test/results/clientpositive/lexplode.q.out (working copy) @@ -0,0 +1,22 @@ +PREHOOK: query: SELECT left_explode(col) AS myCol FROM + (SELECT array(1,2,3) AS col FROM src LIMIT 1 ) a +PREHOOK: type: QUERY +PREHOOK: Input: default@src +#### A masked pattern was here #### +POSTHOOK: query: SELECT left_explode(col) AS myCol FROM + (SELECT array(1,2,3) AS col FROM src LIMIT 1 ) a +POSTHOOK: type: QUERY +POSTHOOK: Input: default@src +#### A masked pattern was here #### +1 +2 +3 +PREHOOK: query: SELECT left_explode(null) AS col FROM src LIMIT 1 +PREHOOK: type: QUERY +PREHOOK: Input: default@src +#### A masked pattern was here #### +POSTHOOK: query: SELECT left_explode(null) AS col FROM src LIMIT 1 +POSTHOOK: type: QUERY +POSTHOOK: Input: default@src +#### A masked pattern was here #### +NULL Index: ql/src/test/results/clientpositive/show_functions.q.out =================================================================== --- ql/src/test/results/clientpositive/show_functions.q.out (revision 1483152) +++ ql/src/test/results/clientpositive/show_functions.q.out (working copy) @@ -90,6 +90,7 @@ last_value lcase lead +left_explode length like ln @@ -227,6 +228,7 @@ json_tuple last_value lcase +left_explode like locate minute