Skip to content

Commit

Permalink
[SPARK-37533][SQL][FOLLOWUP] try_element_at should throw an error on …
Browse files Browse the repository at this point in the history
…0 array index

### What changes were proposed in this pull request?

Change the new function `try_element_at` to throw an error on 0 array index

### Why are the changes needed?

As srielau points out in apache@f705e52#commitcomment-61541454, array index can't be 0 in `element_at`/`try_element_at` and we should throw an error to remind users.

### Does this PR introduce _any_ user-facing change?

No, the new function is not released yet.

### How was this patch tested?

Unit tests

Closes apache#34833 from gengliangwang/try_element_at_followup.

Authored-by: Gengliang Wang <[email protected]>
Signed-off-by: Hyukjin Kwon <[email protected]>
  • Loading branch information
gengliangwang authored and HyukjinKwon committed Dec 8, 2021
1 parent 1fac7a9 commit cf19cf5
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -124,41 +124,3 @@ case class TryDivide(left: Expression, right: Expression, child: Expression)
override protected def withNewChildInternal(newChild: Expression): Expression =
this.copy(child = newChild)
}

/**
* Returns the value of index `right` in Array `left` or the value for key `right` in Map `left`.
* The function is identical to the function `element_at`, except that it returns `NULL` result
* instead of throwing an exception on array's index out of bound or map's key not found.
*/
@ExpressionDescription(
usage = """
_FUNC_(array, index) - Returns element of array at given (1-based) index. If index < 0,
accesses elements from the last to the first. The function always returns NULL
if the index exceeds the length of the array.
_FUNC_(map, key) - Returns value for given key. The function always returns NULL
if the key is not contained in the map.
""",
examples = """
Examples:
> SELECT _FUNC_(array(1, 2, 3), 2);
2
> SELECT _FUNC_(map(1, 'a', 2, 'b'), 2);
b
""",
since = "3.3.0",
group = "map_funcs")
case class TryElementAt(left: Expression, right: Expression, child: Expression)
extends RuntimeReplaceable {
def this(left: Expression, right: Expression) =
this(left, right, TryEval(ElementAt(left, right, failOnError = false)))

override def flatArguments: Iterator[Any] = Iterator(left, right)

override def exprsReplaced: Seq[Expression] = Seq(left, right)

override def prettyName: String = "try_element_at"

override protected def withNewChildInternal(newChild: Expression): Expression =
this.copy(child = newChild)
}
Original file line number Diff line number Diff line change
Expand Up @@ -1996,9 +1996,10 @@ case class ArrayPosition(left: Expression, right: Expression)
*/
@ExpressionDescription(
usage = """
_FUNC_(array, index) - Returns element of array at given (1-based) index. If index < 0,
accesses elements from the last to the first. The function returns NULL
if the index exceeds the length of the array and `spark.sql.ansi.enabled` is set to false.
_FUNC_(array, index) - Returns element of array at given (1-based) index. If Index is 0,
Spark will throw an error. If index < 0, accesses elements from the last to the first.
The function returns NULL if the index exceeds the length of the array and
`spark.sql.ansi.enabled` is set to false.
If `spark.sql.ansi.enabled` is set to true, it throws ArrayIndexOutOfBoundsException
for invalid indices.
Expand Down Expand Up @@ -2172,6 +2173,45 @@ case class ElementAt(
newLeft: Expression, newRight: Expression): ElementAt = copy(left = newLeft, right = newRight)
}

/**
* Returns the value of index `right` in Array `left` or the value for key `right` in Map `left`.
* The function is identical to the function `element_at`, except that it returns `NULL` result
* instead of throwing an exception on array's index out of bound or map's key not found when
* `spark.sql.ansi.enabled` is true.
*/
@ExpressionDescription(
usage = """
_FUNC_(array, index) - Returns element of array at given (1-based) index. If Index is 0,
Spark will throw an error. If index < 0, accesses elements from the last to the first.
The function always returns NULL if the index exceeds the length of the array.
_FUNC_(map, key) - Returns value for given key. The function always returns NULL
if the key is not contained in the map.
""",
examples = """
Examples:
> SELECT _FUNC_(array(1, 2, 3), 2);
2
> SELECT _FUNC_(map(1, 'a', 2, 'b'), 2);
b
""",
since = "3.3.0",
group = "map_funcs")
case class TryElementAt(left: Expression, right: Expression, child: Expression)
extends RuntimeReplaceable {
def this(left: Expression, right: Expression) =
this(left, right, ElementAt(left, right, failOnError = false))

override def flatArguments: Iterator[Any] = Iterator(left, right)

override def exprsReplaced: Seq[Expression] = Seq(left, right)

override def prettyName: String = "try_element_at"

override protected def withNewChildInternal(newChild: Expression): Expression =
this.copy(child = newChild)
}

/**
* Concatenates multiple input columns together into a single column.
* The function works with strings, binary and compatible array columns.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
-- !query
SELECT try_element_at(array(1, 2, 3), 0)
-- !query schema
struct<try_element_at(array(1, 2, 3), 0):int>
struct<>
-- !query output
NULL
java.lang.ArrayIndexOutOfBoundsException
SQL array indices start at 1


-- !query
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
-- !query
SELECT try_element_at(array(1, 2, 3), 0)
-- !query schema
struct<try_element_at(array(1, 2, 3), 0):int>
struct<>
-- !query output
NULL
java.lang.ArrayIndexOutOfBoundsException
SQL array indices start at 1


-- !query
Expand Down

0 comments on commit cf19cf5

Please sign in to comment.