Skip to content

HIVE-27663 - implement array_position UDF in Hive#4653

Merged
saihemanth-cloudera merged 6 commits into
apache:masterfrom
tarak271:tarak-HIVE-27663
Mar 6, 2024
Merged

HIVE-27663 - implement array_position UDF in Hive#4653
saihemanth-cloudera merged 6 commits into
apache:masterfrom
tarak271:tarak-HIVE-27663

Conversation

@tarak271

@tarak271 tarak271 commented Sep 1, 2023

Copy link
Copy Markdown
Contributor

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

public abstract class AbstractGenericUDFArrayBase extends GenericUDF {

static final int ARRAY_IDX = 0;
static final int ELEMENT_IDX = 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?).

@tarak271 tarak271 Sep 6, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may not need to copy the original object here because we don't return the reference of resultArray.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the first condition should be arrayOI.getListLength(array) < 0.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@tarak271

tarak271 commented Sep 6, 2023

Copy link
Copy Markdown
Contributor Author

@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

@sonarqubecloud

sonarqubecloud Bot commented Sep 6, 2023

Copy link
Copy Markdown

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 5 Code Smells

No Coverage information No Coverage information
No Duplication information No Duplication information

warning The version of Java (11.0.8) you have used to run this analysis is deprecated and we will stop accepting it soon. Please update to at least Java 17.
Read more here

@tarak271

Copy link
Copy Markdown
Contributor Author

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",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is examples when we need this one?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);

@okumin okumin Sep 30, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"]  |
+--------------------+----------------+

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. Changed to use ObjectInspector.compare

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix the rest of the array UDFs in https://issues.apache.org/jira/browse/HIVE-27681

@tarak271

Copy link
Copy Markdown
Contributor Author

@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;

@okumin okumin Oct 24, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason why we need this? I feel this is a little risky since it affects several places.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Varchar and string should be convertible and be able to compare. Moving this to base class of Array UDFs, AbstractGenericUDFArrayBase

@sonarqubecloud

Copy link
Copy Markdown

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 8 Code Smells

No Coverage information No Coverage information
No Duplication information No Duplication information

warning The version of Java (11.0.8) you have used to run this analysis is deprecated and we will stop accepting it soon. Please update to at least Java 17.
Read more here

@okumin okumin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I confirmed all results are consistent with Spark SQL 3.4.1.

@sonarqubecloud

sonarqubecloud Bot commented Mar 5, 2024

Copy link
Copy Markdown

Quality Gate Passed Quality Gate passed

Issues
2 New issues
0 Accepted issues

Measures
0 Security Hotspots
No data about Coverage
No data about Duplication

See analysis details on SonarCloud

@saihemanth-cloudera
saihemanth-cloudera merged commit f4e4114 into apache:master Mar 6, 2024
dengzhhu653 pushed a commit to dengzhhu653/hive that referenced this pull request Mar 7, 2024
…ka Rama Rao Lethavadla, Reviewed by Okumin, Sai Hemanth Gantasala)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants