HIVE-27663 - implement array_position UDF in Hive#4653
Conversation
| public abstract class AbstractGenericUDFArrayBase extends GenericUDF { | ||
|
|
||
| static final int ARRAY_IDX = 0; | ||
| static final int ELEMENT_IDX = 1; |
There was a problem hiding this comment.
I guess it is more flexible if checkValueAndListElementTypes accepts an index than we hardcode the index here.
For example, array_insert of Spark SQL receives an element in the 3rd argument, not the 2nd.
https://spark.apache.org/docs/latest/api/sql/index.html#array_insert
Another option is we keep only checkValueAndListElementTypes(arrayElementOI, valueOI), removing checkValueAndListElementTypes(arguments, index?).
There was a problem hiding this comment.
Yes, keeping only checkValueAndListElementTypes(arrayElementOI, valueOI, elementIndex) method with index argument
| */ | ||
| @Description(name = "array_position", value = "_FUNC_(array, element) - Returns the position of the first occurrence of " | ||
| + "element in array. Array indexing starts at 1. If the element value is NULL, a NULL is returned.", extended = | ||
| "Example:\n" + " > SELECT _FUNC_(array(1, 2, 3,4,2), 2) FROM src;\n" + " 2") |
There was a problem hiding this comment.
I originally thought arrays of Hive, Spark, MySQL, etc. were 0-based. But I also see array_position of Spark is 1-based...
spark-sql (default)> SELECT array(1, 2, 3, 4, 5)[3];
4
spark-sql (default)> SELECT array_position(array(1, 2, 3, 4, 5), 3);
3
There was a problem hiding this comment.
Yes, tried to replicate spark's behaviour
| if (arrayOI.getListLength(array) <= 0 || value == null) { | ||
| return null; | ||
| } | ||
| List<?> resultArray = new ArrayList<>(((ListObjectInspector) argumentOIs[ARRAY_IDX]).getList(array)); |
There was a problem hiding this comment.
We may not need to copy the original object here because we don't return the reference of resultArray.
| public Object evaluate(DeferredObject[] arguments) throws HiveException { | ||
| Object array = arguments[ARRAY_IDX].get(); | ||
| Object value = arguments[ELEMENT_IDX].get(); | ||
| if (arrayOI.getListLength(array) <= 0 || value == null) { |
There was a problem hiding this comment.
I guess the first condition should be arrayOI.getListLength(array) < 0.
|
@okumin I have updated logic to deal with array of Varchar types. Will create another pull request with these varchar changes to the rest of array udfs |
|
Kudos, SonarCloud Quality Gate passed!
|
|
Created https://issues.apache.org/jira/browse/HIVE-27681 to handle varchar data type in all other Array UDFs. @okumin @saihemanth-cloudera need your help in review and merge |
| // Check if list element and value are of same type | ||
| if (!ObjectInspectorUtils.compareTypes(arrayElementOI, valueOI)) { | ||
| throw new UDFArgumentTypeException(elementIndex, | ||
| String.format("%s type element is expected at function array_position(array<%s>,%s), but %s is found", |
There was a problem hiding this comment.
Sorry, I overlooked that this also contains array_position(array<%s>, %s). Maybe, that part, at least the function name, has to be a placeholder.
There was a problem hiding this comment.
My bad, created placefolder for function name as well
| } | ||
| List<?> resultArray = ((ListObjectInspector) argumentOIs[ARRAY_IDX]).getList(array); | ||
| // Handling Varchar type this way as Object comparison between string and varchar will not work | ||
| if ((argumentOIs[ELEMENT_IDX].getTypeName().contains(serdeConstants.VARCHAR_TYPE_NAME) |
There was a problem hiding this comment.
What is examples when we need this one?
There was a problem hiding this comment.
The below example is for a different UDF, but applies to this as well
CREATE TABLE temp (top ARRAY<STRING>);
insert into temp values(array(cast('true' as VARCHAR(10))));
select array_contains(top,cast(true as VARCHAR(10))) from temp;
| } | ||
| } | ||
| } | ||
| return new IntWritable(resultArray.indexOf(value) + 1); |
There was a problem hiding this comment.
We might have assumed the returned value of ListObjectInspector#getList can be directly used. But I started feeling it is still an abstract value, and then we need to parse them via the object inspector of the element.
https://github.com/apache/hive/blob/rel/release-4.0.0-beta-1/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFArrayContains.java#L121-L122
This is the example where I compared arrays in a table and arrays or an element as a literal. It could be better to check other similar UDFs that have been merged first.
0: jdbc:hive2://hive-hiveserver2:10000/defaul> create table test as select array('a', 'b', 'c', 'b') as a union all select array('a', 'c', 'd') as a;
...
0: jdbc:hive2://hive-hiveserver2:10000/defaul> select a, array_contains(a, 'b') from test;
+--------------------+--------+
| a | _c1 |
+--------------------+--------+
| ["a","b","c","b"] | true |
| ["a","c","d"] | false |
+--------------------+--------+
...
0: jdbc:hive2://hive-hiveserver2:10000/defaul> select a, array_position(a, 'b') from test;
+--------------------+------+
| a | _c1 |
+--------------------+------+
| ["a","b","c","b"] | 0 |
| ["a","c","d"] | 0 |
+--------------------+------+
0: jdbc:hive2://hive-hiveserver2:10000/defaul> select a, array_except(a, array('a', 'd')) from test;
...
+--------------------+----------------+
| a | _c1 |
+--------------------+----------------+
| ["a","b","c","b"] | ["a","b","c"] |
| ["a","c","d"] | ["a","c","d"] |
+--------------------+----------------+
There was a problem hiding this comment.
Understood. Changed to use ObjectInspector.compare
There was a problem hiding this comment.
Will fix the rest of the array UDFs in https://issues.apache.org/jira/browse/HIVE-27681
|
@okumin @saihemanth-cloudera need your help in review and merge |
| && poi2.getPrimitiveCategory().equals(PrimitiveObjectInspector.PrimitiveCategory.STRING)) || ( | ||
| poi1.getPrimitiveCategory().equals(PrimitiveObjectInspector.PrimitiveCategory.STRING) | ||
| && poi2.getPrimitiveCategory().equals(PrimitiveObjectInspector.PrimitiveCategory.VARCHAR))) { | ||
| return true; |
There was a problem hiding this comment.
What's the reason why we need this? I feel this is a little risky since it affects several places.
There was a problem hiding this comment.
Varchar and string should be convertible and be able to compare. Moving this to base class of Array UDFs, AbstractGenericUDFArrayBase
|
Kudos, SonarCloud Quality Gate passed!
|
9670184 to
aff09f0
Compare
okumin
left a comment
There was a problem hiding this comment.
I would say the current implementation doesn't have any defects. I'm looking forward to using it
| @@ -0,0 +1,172 @@ | |||
| PREHOOK: query: DESCRIBE FUNCTION array_position | |||
There was a problem hiding this comment.
I confirmed all results are consistent with Spark SQL 3.4.1.
|
…ka Rama Rao Lethavadla, Reviewed by Okumin, Sai Hemanth Gantasala)













What changes were proposed in this pull request?
Implement array_position function in Hive
Why are the changes needed?
This enhancement is already implemented in Spark
Does this PR introduce any user-facing change?
No
Is the change a dependency upgrade?
No
How was this patch tested?
Created Junit tests as well as qtests as part of this change