diff --git a/extensions/functions_list.yaml b/extensions/functions_list.yaml index f7776350f..9dcf5ed12 100644 --- a/extensions/functions_list.yaml +++ b/extensions/functions_list.yaml @@ -130,3 +130,32 @@ scalar_functions: value: func boolean?> nullability: DECLARED_OUTPUT return: boolean? + + - name: "subscript_operator" + description: >- + Returns the element at the specified index from the list, corresponding + to the common array subscript operator []. + + Index is 1-based (i.e., the first element is at index 1). Out-of-bounds + access (index <= 0 or index > list length) returns null. + impls: + - args: + - name: input + value: list + - name: index + value: i64 + nullability: DECLARED_OUTPUT + return: any1? + + - name: "index_of" + description: >- + Returns the 1-based index of the first occurrence of the specified value + in the list, or null if the value is not found. + impls: + - args: + - name: input + value: list + - name: value + value: any1 + nullability: DECLARED_OUTPUT + return: i64? diff --git a/tests/cases/list/index_of.test b/tests/cases/list/index_of.test new file mode 100644 index 000000000..3adbf60e2 --- /dev/null +++ b/tests/cases/list/index_of.test @@ -0,0 +1,27 @@ +### SUBSTRAIT_SCALAR_TEST: v1.0 +### SUBSTRAIT_INCLUDE: '/extensions/functions_list.yaml' + +# basic: Find element in list (1-based index) +index_of([10, 20, 30]::list, 10::i32) = 1::i64? +index_of([10, 20, 30]::list, 20::i32) = 2::i64? +index_of([10, 20, 30]::list, 30::i32) = 3::i64? +index_of(['a', 'b', 'c']::list, 'b'::string) = 2::i64? + +# not_found: Element not in list returns null +index_of([10, 20, 30]::list, 5::i32) = null::i64? +index_of([10, 20, 30]::list, 100::i32) = null::i64? + +# first_match: Returns index of first occurrence +index_of([10, 20, 20, 30]::list, 20::i32) = 2::i64? + +# empty_list: Empty list returns null +index_of([]::list, 1::i32) = null::i64? + +# null_list: Null list returns null +index_of(null::list?, 1::i32) = null::i64? + +# null_value: Searching for null returns null +index_of([10, 20, 30]::list, null::i32?) = null::i64? + +# find_null_element: Finding null in list with nulls +index_of([1, null, 3]::list, null::i32?) = 2::i64? diff --git a/tests/cases/list/subscript_operator.test b/tests/cases/list/subscript_operator.test new file mode 100644 index 000000000..ad41f11d4 --- /dev/null +++ b/tests/cases/list/subscript_operator.test @@ -0,0 +1,31 @@ +### SUBSTRAIT_SCALAR_TEST: v1.0 +### SUBSTRAIT_INCLUDE: '/extensions/functions_list.yaml' + +# basic: Basic positive index (1-based) +subscript_operator([10, 20, 30]::list, 1::i64) = 10::i32? +subscript_operator([10, 20, 30]::list, 2::i64) = 20::i32? +subscript_operator([10, 20, 30]::list, 3::i64) = 30::i32? +subscript_operator(['a', 'b', 'c']::list, 2::i64) = 'b'::string? + +# out_of_bounds_positive: Positive index exceeds list length +subscript_operator([10, 20, 30]::list, 5::i64) = null::i32? +subscript_operator([10, 20, 30]::list, 100::i64) = null::i32? + +# zero_out_of_bounds: Zero index is out of bounds +subscript_operator([10, 20, 30]::list, 0::i64) = null::i32? + +# negative_out_of_bounds: Negative indices are out of bounds +subscript_operator([10, 20, 30]::list, -1::i64) = null::i32? +subscript_operator([10, 20, 30]::list, -3::i64) = null::i32? + +# null_list: Null list returns null +subscript_operator(null::list?, 1::i64) = null::i32? + +# null_index: Null index returns null +subscript_operator([10, 20, 30]::list, null::i64?) = null::i32? + +# null_element: Retrieving a null element returns null +subscript_operator([1, null, 3]::list, 2::i64) = null::i32? + +# empty_list: Out of bounds on empty list +subscript_operator([]::list, 1::i64) = null::i32?