diff --git a/cpp/src/arrow/compute/kernels/scalar_set_lookup.cc b/cpp/src/arrow/compute/kernels/scalar_set_lookup.cc index c6a3562233c..8cf38919334 100644 --- a/cpp/src/arrow/compute/kernels/scalar_set_lookup.cc +++ b/cpp/src/arrow/compute/kernels/scalar_set_lookup.cc @@ -222,6 +222,8 @@ struct InitStateVisitor { ty1, " and ", ty2); } } else if ((arg_type.id() == Type::STRING || arg_type.id() == Type::LARGE_STRING) && + !(options.value_set.type()->id() == Type::NA && + options.value_set.length() == 0) && !is_base_binary_like(options.value_set.type()->id())) { // This is a bit of a hack, but don't implicitly cast from a non-binary // type to string, since most types support casting to string and that diff --git a/cpp/src/arrow/compute/kernels/scalar_set_lookup_test.cc b/cpp/src/arrow/compute/kernels/scalar_set_lookup_test.cc index dbd89489920..ad377813438 100644 --- a/cpp/src/arrow/compute/kernels/scalar_set_lookup_test.cc +++ b/cpp/src/arrow/compute/kernels/scalar_set_lookup_test.cc @@ -203,6 +203,12 @@ TEST_F(TestIsInKernel, ImplicitlyCastValueSet) { ArrayFromJSON(utf8(), R"(["aaa", "bbb"])"), "[true, true, false, false, true]"); + // An empty null value set can be safely cast to the input string type. + CheckIsIn(ArrayFromJSON(utf8(), R"(["aaa", "bbb"])"), ArrayFromJSON(null(), "[]"), + "[false, false]"); + CheckIsIn(ArrayFromJSON(large_utf8(), R"(["aaa", "bbb"])"), ArrayFromJSON(null(), "[]"), + "[false, false]"); + // But explicitly deny implicit casts from non-binary to utf8 to // avoid surprises ASSERT_RAISES(TypeError, @@ -1455,6 +1461,13 @@ TEST_F(TestIndexInKernel, ImplicitlyCastValueSet) { ArrayFromJSON(large_utf8(), R"(["aaa", "bbb"])"), "[0, 1, null, null, 1]"); CheckIndexIn(ArrayFromJSON(large_utf8(), R"(["aaa", "bbb", "ccc", null, "bbb"])"), ArrayFromJSON(utf8(), R"(["aaa", "bbb"])"), "[0, 1, null, null, 1]"); + + // An empty null value set can be safely cast to the input string type. + CheckIndexIn(ArrayFromJSON(utf8(), R"(["aaa", "bbb"])"), ArrayFromJSON(null(), "[]"), + "[null, null]"); + CheckIndexIn(ArrayFromJSON(large_utf8(), R"(["aaa", "bbb"])"), + ArrayFromJSON(null(), "[]"), "[null, null]"); + // But explicitly deny implicit casts from non-binary to utf8 to // avoid surprises ASSERT_RAISES(TypeError, diff --git a/python/pyarrow/tests/test_compute.py b/python/pyarrow/tests/test_compute.py index d350c811579..9149d9e5475 100644 --- a/python/pyarrow/tests/test_compute.py +++ b/python/pyarrow/tests/test_compute.py @@ -1730,6 +1730,17 @@ def test_filter_table(): assert result.equals(expected_null) +@pytest.mark.parametrize("dictionary_encode", [False, True], + ids=["string", "dictionary-string"]) +def test_filter_table_expression_empty_set(dictionary_encode): + values = pa.array(["a", "b"]) + if dictionary_encode: + values = values.dictionary_encode() + + table = pa.table({"a": values}) + assert table.filter(pc.field("a").isin([])).equals(table.slice(0, 0)) + + def test_filter_errors(): arr = pa.chunked_array([["a", None], ["c", "d", "e"]]) batch = pa.record_batch(