diff --git a/presto-docs/src/main/sphinx/functions/array.rst b/presto-docs/src/main/sphinx/functions/array.rst index d345541ace037..9d825d874b6be 100644 --- a/presto-docs/src/main/sphinx/functions/array.rst +++ b/presto-docs/src/main/sphinx/functions/array.rst @@ -90,6 +90,12 @@ Array Functions Returns the position of the first occurrence of the ``element`` in array ``x`` (or 0 if not found). +.. function:: array_position(x, element, instance) -> bigint + + If ``instance > 0``, returns the position of the `instance`-th occurrence of the ``element`` in array ``x``. If + ``instance < 0``, returns the position of the ``instance``-to-last occurrence of the ``element`` in array ``x``. + If no matching element instance is found, ``0`` is returned. + .. function:: array_remove(x, element) -> array Remove all elements that equal ``element`` from array ``x``. diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/BuiltInTypeAndFunctionNamespaceManager.java b/presto-main/src/main/java/com/facebook/presto/metadata/BuiltInTypeAndFunctionNamespaceManager.java index e5e7682380bd7..59183c42f5cd2 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/BuiltInTypeAndFunctionNamespaceManager.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/BuiltInTypeAndFunctionNamespaceManager.java @@ -109,6 +109,7 @@ import com.facebook.presto.operator.scalar.ArrayNormalizeFunction; import com.facebook.presto.operator.scalar.ArrayNotEqualOperator; import com.facebook.presto.operator.scalar.ArrayPositionFunction; +import com.facebook.presto.operator.scalar.ArrayPositionWithIndexFunction; import com.facebook.presto.operator.scalar.ArrayRemoveFunction; import com.facebook.presto.operator.scalar.ArrayReverseFunction; import com.facebook.presto.operator.scalar.ArrayShuffleFunction; @@ -711,6 +712,7 @@ private List getBuildInFunctions(FeaturesConfig featuresC .scalar(ArrayContains.class) .scalar(ArrayFilterFunction.class) .scalar(ArrayPositionFunction.class) + .scalar(ArrayPositionWithIndexFunction.class) .scalars(CombineHashFunction.class) .scalars(JsonOperators.class) .scalar(JsonOperators.JsonDistinctFromOperator.class) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayPositionFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayPositionFunction.java index 10835b383bf43..3aa5459d636b7 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayPositionFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayPositionFunction.java @@ -16,7 +16,6 @@ import com.facebook.presto.common.block.Block; import com.facebook.presto.common.type.StandardTypes; import com.facebook.presto.common.type.Type; -import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.function.Description; import com.facebook.presto.spi.function.OperatorDependency; import com.facebook.presto.spi.function.ScalarFunction; @@ -27,13 +26,11 @@ import java.lang.invoke.MethodHandle; import static com.facebook.presto.common.function.OperatorType.EQUAL; -import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; -import static com.facebook.presto.util.Failures.internalError; -import static com.google.common.base.Verify.verify; @Description("Returns the position of the first occurrence of the given value in array (or 0 if not found)") @ScalarFunction("array_position") public final class ArrayPositionFunction + extends ArrayPositionWithIndexFunction { private ArrayPositionFunction() {} @@ -45,23 +42,7 @@ public static long arrayPosition( @SqlType("array(T)") Block array, @SqlType("T") boolean element) { - int size = array.getPositionCount(); - for (int i = 0; i < size; i++) { - if (!array.isNull(i)) { - boolean arrayValue = type.getBoolean(array, i); - try { - Boolean result = (Boolean) equalMethodHandle.invokeExact(arrayValue, element); - verify(result != null, "Array element should not be null"); - if (result) { - return i + 1; // result is 1-based (instead of 0) - } - } - catch (Throwable t) { - throw internalError(t); - } - } - } - return 0; + return arrayPositionWithIndex(type, equalMethodHandle, array, element, 1); } @TypeParameter("T") @@ -72,23 +53,7 @@ public static long arrayPosition( @SqlType("array(T)") Block array, @SqlType("T") long element) { - int size = array.getPositionCount(); - for (int i = 0; i < size; i++) { - if (!array.isNull(i)) { - long arrayValue = type.getLong(array, i); - try { - Boolean result = (Boolean) equalMethodHandle.invokeExact(arrayValue, element); - verify(result != null, "Array element should not be null"); - if (result) { - return i + 1; // result is 1-based (instead of 0) - } - } - catch (Throwable t) { - throw internalError(t); - } - } - } - return 0; + return arrayPositionWithIndex(type, equalMethodHandle, array, element, 1); } @TypeParameter("T") @@ -99,23 +64,7 @@ public static long arrayPosition( @SqlType("array(T)") Block array, @SqlType("T") double element) { - int size = array.getPositionCount(); - for (int i = 0; i < size; i++) { - if (!array.isNull(i)) { - double arrayValue = type.getDouble(array, i); - try { - Boolean result = (Boolean) equalMethodHandle.invokeExact(arrayValue, element); - verify(result != null, "Array element should not be null"); - if (result) { - return i + 1; // result is 1-based (instead of 0) - } - } - catch (Throwable t) { - throw internalError(t); - } - } - } - return 0; + return arrayPositionWithIndex(type, equalMethodHandle, array, element, 1); } @TypeParameter("T") @@ -126,23 +75,7 @@ public static long arrayPosition( @SqlType("array(T)") Block array, @SqlType("T") Slice element) { - int size = array.getPositionCount(); - for (int i = 0; i < size; i++) { - if (!array.isNull(i)) { - Slice arrayValue = type.getSlice(array, i); - try { - Boolean result = (Boolean) equalMethodHandle.invokeExact(arrayValue, element); - verify(result != null, "Array element should not be nul"); - if (result) { - return i + 1; // result is 1-based (instead of 0) - } - } - catch (Throwable t) { - throw internalError(t); - } - } - } - return 0; + return arrayPositionWithIndex(type, equalMethodHandle, array, element, 1); } @TypeParameter("T") @@ -153,24 +86,6 @@ public static long arrayPosition( @SqlType("array(T)") Block array, @SqlType("T") Block element) { - int size = array.getPositionCount(); - for (int i = 0; i < size; i++) { - if (!array.isNull(i)) { - Object arrayValue = type.getObject(array, i); - try { - Boolean result = (Boolean) equalMethodHandle.invoke(arrayValue, element); - if (result == null) { - throw new PrestoException(NOT_SUPPORTED, "array_position does not support elements of complex types that contain null"); - } - if (result) { - return i + 1; // result is 1-based (instead of 0) - } - } - catch (Throwable t) { - throw internalError(t); - } - } - } - return 0; + return arrayPositionWithIndex(type, equalMethodHandle, array, element, 1); } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayPositionWithIndexFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayPositionWithIndexFunction.java new file mode 100644 index 0000000000000..8bb83b5edd2dd --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayPositionWithIndexFunction.java @@ -0,0 +1,256 @@ +/* + * Licensed 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 com.facebook.presto.operator.scalar; + +import com.facebook.presto.common.block.Block; +import com.facebook.presto.common.type.StandardTypes; +import com.facebook.presto.common.type.Type; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.function.Description; +import com.facebook.presto.spi.function.OperatorDependency; +import com.facebook.presto.spi.function.ScalarFunction; +import com.facebook.presto.spi.function.SqlType; +import com.facebook.presto.spi.function.TypeParameter; +import io.airlift.slice.Slice; + +import java.lang.invoke.MethodHandle; + +import static com.facebook.presto.common.function.OperatorType.EQUAL; +import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; +import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; +import static com.facebook.presto.util.Failures.internalError; + +@Description("Returns the position of the first occurrence of the given value in array (or 0 if not found)") +@ScalarFunction("array_position") +public class ArrayPositionWithIndexFunction +{ + protected ArrayPositionWithIndexFunction() {} + + @TypeParameter("T") + @SqlType(StandardTypes.BIGINT) + public static long arrayPositionWithIndex( + @TypeParameter("T") Type type, + @OperatorDependency(operator = EQUAL, argumentTypes = {"T", "T"}) MethodHandle equalMethodHandle, + @SqlType("array(T)") Block array, + @SqlType("T") boolean element, + @SqlType(StandardTypes.BIGINT) long instance) + { + int size = array.getPositionCount(); + int instancesFound = 0; + + if (instance == 0) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "0 is an invalid instance position for array_position."); + } + + int startIndex = instance > 0 ? 0 : size - 1; + int stopIndex = instance > 0 ? size : -1; + int stepSize = instance > 0 ? 1 : -1; + instance = Math.abs(instance); + + for (int i = startIndex; i != stopIndex; i += stepSize) { + if (!array.isNull(i)) { + try { + boolean arrayValue = type.getBoolean(array, i); + Boolean result = (Boolean) equalMethodHandle.invoke(arrayValue, element); + checkNotIndeterminate(result); + if (result) { + instancesFound++; + if (instancesFound == instance) { + return i + 1; // result is 1-based (instead of 0) + } + } + } + catch (Throwable t) { + throw internalError(t); + } + } + } + return 0; + } + + @TypeParameter("T") + @SqlType(StandardTypes.BIGINT) + public static long arrayPositionWithIndex( + @TypeParameter("T") Type type, + @OperatorDependency(operator = EQUAL, argumentTypes = {"T", "T"}) MethodHandle equalMethodHandle, + @SqlType("array(T)") Block array, + @SqlType("T") long element, + @SqlType(StandardTypes.BIGINT) long instance) + { + int size = array.getPositionCount(); + int instancesFound = 0; + + if (instance == 0) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "array_position cannot take a 0-valued instance argument."); + } + + int startIndex = instance > 0 ? 0 : size - 1; + int stopIndex = instance > 0 ? size : -1; + int stepSize = instance > 0 ? 1 : -1; + instance = Math.abs(instance); + + for (int i = startIndex; i != stopIndex; i += stepSize) { + if (!array.isNull(i)) { + try { + long arrayValue = type.getLong(array, i); + Boolean result = (Boolean) equalMethodHandle.invoke(arrayValue, element); + checkNotIndeterminate(result); + if (result) { + instancesFound++; + if (instancesFound == instance) { + return i + 1; // result is 1-based (instead of 0) + } + } + } + catch (Throwable t) { + throw internalError(t); + } + } + } + return 0; + } + + @TypeParameter("T") + @SqlType(StandardTypes.BIGINT) + public static long arrayPositionWithIndex( + @TypeParameter("T") Type type, + @OperatorDependency(operator = EQUAL, argumentTypes = {"T", "T"}) MethodHandle equalMethodHandle, + @SqlType("array(T)") Block array, + @SqlType("T") double element, + @SqlType(StandardTypes.BIGINT) long instance) + { + int size = array.getPositionCount(); + int instancesFound = 0; + + if (instance == 0) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "array_position cannot take a 0-valued instance argument."); + } + + int startIndex = instance > 0 ? 0 : size - 1; + int stopIndex = instance > 0 ? size : -1; + int stepSize = instance > 0 ? 1 : -1; + instance = Math.abs(instance); + + for (int i = startIndex; i != stopIndex; i += stepSize) { + if (!array.isNull(i)) { + try { + double arrayValue = type.getDouble(array, i); + Boolean result = (Boolean) equalMethodHandle.invoke(arrayValue, element); + checkNotIndeterminate(result); + if (result) { + instancesFound++; + if (instancesFound == instance) { + return i + 1; // result is 1-based (instead of 0) + } + } + } + catch (Throwable t) { + throw internalError(t); + } + } + } + return 0; + } + + @TypeParameter("T") + @SqlType(StandardTypes.BIGINT) + public static long arrayPositionWithIndex( + @TypeParameter("T") Type type, + @OperatorDependency(operator = EQUAL, argumentTypes = {"T", "T"}) MethodHandle equalMethodHandle, + @SqlType("array(T)") Block array, + @SqlType("T") Slice element, + @SqlType(StandardTypes.BIGINT) long instance) + { + int size = array.getPositionCount(); + int instancesFound = 0; + + if (instance == 0) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "array_position cannot take a 0-valued instance argument."); + } + + int startIndex = instance > 0 ? 0 : size - 1; + int stopIndex = instance > 0 ? size : -1; + int stepSize = instance > 0 ? 1 : -1; + instance = Math.abs(instance); + + for (int i = startIndex; i != stopIndex; i += stepSize) { + if (!array.isNull(i)) { + try { + Slice arrayValue = type.getSlice(array, i); + Boolean result = (Boolean) equalMethodHandle.invoke(arrayValue, element); + checkNotIndeterminate(result); + if (result) { + instancesFound++; + if (instancesFound == instance) { + return i + 1; // result is 1-based (instead of 0) + } + } + } + catch (Throwable t) { + throw internalError(t); + } + } + } + return 0; + } + + @TypeParameter("T") + @SqlType(StandardTypes.BIGINT) + public static long arrayPositionWithIndex( + @TypeParameter("T") Type type, + @OperatorDependency(operator = EQUAL, argumentTypes = {"T", "T"}) MethodHandle equalMethodHandle, + @SqlType("array(T)") Block array, + @SqlType("T") Block element, + @SqlType(StandardTypes.BIGINT) long instance) + { + int size = array.getPositionCount(); + int instancesFound = 0; + + if (instance == 0) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "array_position cannot take a 0-valued instance argument."); + } + + int startIndex = instance > 0 ? 0 : size - 1; + int stopIndex = instance > 0 ? size : -1; + int stepSize = instance > 0 ? 1 : -1; + instance = Math.abs(instance); + + for (int i = startIndex; i != stopIndex; i += stepSize) { + if (!array.isNull(i)) { + try { + Object arrayValue = type.getObject(array, i); + Boolean result = (Boolean) equalMethodHandle.invoke(arrayValue, element); + checkNotIndeterminate(result); + if (result) { + instancesFound++; + if (instancesFound == instance) { + return i + 1; // result is 1-based (instead of 0) + } + } + } + catch (Throwable t) { + throw internalError(t); + } + } + } + return 0; + } + + private static void checkNotIndeterminate(Boolean equalsResult) + { + if (equalsResult == null) { + throw new PrestoException(NOT_SUPPORTED, "array_position does not support arrays with elements that are null or contain null"); + } + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestArrayOperators.java b/presto-main/src/test/java/com/facebook/presto/type/TestArrayOperators.java index ceb3b34a753ef..8f32682a72965 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestArrayOperators.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestArrayOperators.java @@ -685,6 +685,82 @@ public void testArrayPosition() assertInvalidFunction("ARRAY_POSITION(ARRAY [ARRAY[null]], ARRAY[1])", NOT_SUPPORTED); assertInvalidFunction("ARRAY_POSITION(ARRAY [ARRAY[null]], ARRAY[null])", NOT_SUPPORTED); + + // These should all be valid with respect to the ones above, since 1-index is the default. + assertFunction("ARRAY_POSITION(ARRAY [10, 20, 30, 40], 30, 1)", BIGINT, 3L); + assertFunction("ARRAY_POSITION(CAST (JSON '[]' as array(bigint)), 30, 1)", BIGINT, 0L); + assertFunction("ARRAY_POSITION(ARRAY [cast(NULL as bigint)], 30, 1)", BIGINT, 0L); + assertFunction("ARRAY_POSITION(ARRAY [cast(NULL as bigint), NULL, NULL], 30, 1)", BIGINT, 0L); + assertFunction("ARRAY_POSITION(ARRAY [NULL, NULL, 30, NULL], 30, 1)", BIGINT, 3L); + + assertFunction("ARRAY_POSITION(ARRAY [1.1E0, 2.1E0, 3.1E0, 4.1E0], 3.1E0, 1)", BIGINT, 3L); + assertFunction("ARRAY_POSITION(ARRAY [false, false, true, true], true, 1)", BIGINT, 3L); + assertFunction("ARRAY_POSITION(ARRAY ['10', '20', '30', '40'], '30', 1)", BIGINT, 3L); + + assertFunction("ARRAY_POSITION(ARRAY [DATE '2000-01-01', DATE '2000-01-02', DATE '2000-01-03', DATE '2000-01-04'], DATE '2000-01-03', 1)", BIGINT, 3L); + assertFunction("ARRAY_POSITION(ARRAY [ARRAY [1, 11], ARRAY [2, 12], ARRAY [3, 13], ARRAY [4, 14]], ARRAY [3, 13], 1)", BIGINT, 3L); + + assertFunction("ARRAY_POSITION(ARRAY [], NULL, 1)", BIGINT, null); + assertFunction("ARRAY_POSITION(ARRAY [NULL], NULL, 1)", BIGINT, null); + assertFunction("ARRAY_POSITION(ARRAY [1, NULL, 2], NULL, 1)", BIGINT, null); + assertFunction("ARRAY_POSITION(ARRAY [1, CAST(NULL AS BIGINT), 2], CAST(NULL AS BIGINT), 1)", BIGINT, null); + assertFunction("ARRAY_POSITION(ARRAY [1, NULL, 2], CAST(NULL AS BIGINT), 1)", BIGINT, null); + assertFunction("ARRAY_POSITION(ARRAY [1, CAST(NULL AS BIGINT), 2], NULL, 1)", BIGINT, null); + assertFunction("ARRAY_POSITION(ARRAY [1.0, 2.0, 3.0, 4.0], 3.0, 1)", BIGINT, 3L); + assertFunction("ARRAY_POSITION(ARRAY [1.0, 2.0, 000000000000000000000003.000, 4.0], 3.0, 1)", BIGINT, 3L); + assertFunction("ARRAY_POSITION(ARRAY [1.0, 2.0, 3.0, 4.0], 000000000000000000000003.000, 1)", BIGINT, 3L); + assertFunction("ARRAY_POSITION(ARRAY [1.0, 2.0, 3.0, 4.0], 3, 1)", BIGINT, 3L); + assertFunction("ARRAY_POSITION(ARRAY [1.0, 2.0, 3, 4.0], 4.0, 1)", BIGINT, 4L); + assertFunction("ARRAY_POSITION(ARRAY [ARRAY[1]], ARRAY[1], 1)", BIGINT, 1L); + + assertInvalidFunction("ARRAY_POSITION(ARRAY [ARRAY[null]], ARRAY[1], 1)", NOT_SUPPORTED); + assertInvalidFunction("ARRAY_POSITION(ARRAY [ARRAY[null]], ARRAY[null], 1)", NOT_SUPPORTED); + // End strictly n = 1 checks. + + assertFunction("ARRAY_POSITION(ARRAY[1, 2, 3, 4], 1, -1)", BIGINT, 1L); + assertFunction("ARRAY_POSITION(ARRAY[4, 3, 2, 1], 1, -1)", BIGINT, 4L); + assertFunction("ARRAY_POSITION(ARRAY[1, 2, 3, 4, 1], 1, 2)", BIGINT, 5L); + assertFunction("ARRAY_POSITION(ARRAY[1, 2, 3, 4, 1], 1, -2)", BIGINT, 1L); + assertFunction("ARRAY_POSITION(ARRAY[1, 2, 3, 4, 1], 1, 3)", BIGINT, 0L); + assertFunction("ARRAY_POSITION(ARRAY[1, 2, 3, 4, 1], 1, -3)", BIGINT, 0L); + assertFunction("ARRAY_POSITION(ARRAY[1, 2, 3, 4, 1, 1], 1, 3)", BIGINT, 6L); + assertFunction("ARRAY_POSITION(ARRAY[1, 2, 3, 4, 1, 1], 1, -1)", BIGINT, 6L); + assertFunction("ARRAY_POSITION(ARRAY[1, 2, 3, 4, 1, 1], 1, -3)", BIGINT, 1L); + + assertFunction("ARRAY_POSITION(ARRAY[true, false], true, -1)", BIGINT, 1L); + assertFunction("ARRAY_POSITION(ARRAY[true, false], false, -1)", BIGINT, 2L); + assertFunction("ARRAY_POSITION(ARRAY[true, true, true, true], true, 3)", BIGINT, 3L); + assertFunction("ARRAY_POSITION(ARRAY[true, true, true, true], true, 4)", BIGINT, 4L); + assertFunction("ARRAY_POSITION(ARRAY[true, true, true, true], true, 5)", BIGINT, 0L); + assertFunction("ARRAY_POSITION(ARRAY[true, true, true, true], true, -2)", BIGINT, 3L); + assertFunction("ARRAY_POSITION(ARRAY[true, true, true, true], true, -4)", BIGINT, 1L); + assertFunction("ARRAY_POSITION(ARRAY[true, true, true, true], true, -5)", BIGINT, 0L); + + assertFunction("ARRAY_POSITION(ARRAY[1.0, 2.0, 3.0, 4.0], 1.0, -1)", BIGINT, 1L); + assertFunction("ARRAY_POSITION(ARRAY[1.0, 2.0, 3.0, 4.0], 2.0, -1)", BIGINT, 2L); + assertFunction("ARRAY_POSITION(ARRAY[1.0, 2.0, 1.0, 1.0, 2.0], 1.0, 2)", BIGINT, 3L); + assertFunction("ARRAY_POSITION(ARRAY[1.0, 2.0, 1.0, 1.0, 2.0], 1.0, 3)", BIGINT, 4L); + assertFunction("ARRAY_POSITION(ARRAY[1.0, 2.0, 1.0, 1.0, 2.0], 1.0, -2)", BIGINT, 3L); + assertFunction("ARRAY_POSITION(ARRAY[1.0, 2.0, 1.0, 1.0, 2.0], 1.0, -3)", BIGINT, 1L); + assertFunction("ARRAY_POSITION(ARRAY[1.0, 2.0, 1.0, 1.0, 2.0], 1.0, -6)", BIGINT, 0L); + assertFunction("ARRAY_POSITION(ARRAY[1.0, 2.0, 1.0, 1.0, 2.0], 1.0, 6)", BIGINT, 0L); + + assertFunction("ARRAY_POSITION(ARRAY[ARRAY[1], ARRAY[2], ARRAY[3]], ARRAY[1], -1)", BIGINT, 1L); + assertFunction("ARRAY_POSITION(ARRAY[ARRAY[1], ARRAY[2], ARRAY[3]], ARRAY[3], -1)", BIGINT, 3L); + assertFunction("ARRAY_POSITION(ARRAY[ARRAY[1], ARRAY[2], ARRAY[3], ARRAY[1]], ARRAY[1], -1)", BIGINT, 4L); + assertFunction("ARRAY_POSITION(ARRAY[ARRAY[1], ARRAY[2], ARRAY[3], ARRAY[1]], ARRAY[1], -2)", BIGINT, 1L); + assertFunction("ARRAY_POSITION(ARRAY[ARRAY[1], ARRAY[2], ARRAY[3], ARRAY[1]], ARRAY[1], -3)", BIGINT, 0L); + assertFunction("ARRAY_POSITION(ARRAY[ARRAY[1], ARRAY[2], ARRAY[3], ARRAY[1]], ARRAY[1], 2)", BIGINT, 4L); + assertFunction("ARRAY_POSITION(ARRAY[ARRAY[1], ARRAY[2], ARRAY[3], ARRAY[1]], ARRAY[1], 3)", BIGINT, 0L); + + assertFunction("ARRAY_POSITION(CAST(ARRAY[] AS ARRAY(BIGINT)), 1, -1)", BIGINT, 0L); + assertFunction("ARRAY_POSITION(ARRAY[1, 2, 3, 4], null, 2)", BIGINT, null); + assertFunction("ARRAY_POSITION(ARRAY[1, 2, 3, 4], null, -1)", BIGINT, null); + assertFunction("ARRAY_POSITION(ARRAY[1, 2, 3, 4], null, -4)", BIGINT, null); + + assertInvalidFunction("ARRAY_POSITION(ARRAY [ARRAY[null]], ARRAY[1], -1)", NOT_SUPPORTED); + assertInvalidFunction("ARRAY_POSITION(ARRAY [ARRAY[null]], ARRAY[null], -1)", NOT_SUPPORTED); + assertInvalidFunction("ARRAY_POSITION(ARRAY [1, 2, 3, 4], 4, 0)", INVALID_FUNCTION_ARGUMENT); } @Test