diff --git a/cpp/src/arrow/compute/exec.cc b/cpp/src/arrow/compute/exec.cc index 411ff0bb026..042240b3edf 100644 --- a/cpp/src/arrow/compute/exec.cc +++ b/cpp/src/arrow/compute/exec.cc @@ -783,7 +783,14 @@ class ScalarExecutor : public KernelExecutorImpl { Status Execute(const ExecBatch& batch, ExecListener* listener) override { RETURN_NOT_OK(span_iterator_.Init(batch, exec_context()->exec_chunksize())); - if (batch.length == 0) { + // A dictionary-to-dictionary cast must run to preserve unreferenced dictionary + // values even when there are no indices to process. + const bool changes_dictionary_type = + batch.num_values() == 1 && batch.values[0].type() != nullptr && + is_dictionary(batch.values[0].type()->id()) && + is_dictionary(output_type_.type->id()) && + !batch.values[0].type()->Equals(*output_type_.type); + if (batch.length == 0 && !changes_dictionary_type) { // For zero-length batches, we do nothing except return a zero-length // array of the correct output type ARROW_ASSIGN_OR_RAISE(std::shared_ptr result, diff --git a/cpp/src/arrow/compute/kernels/scalar_cast_test.cc b/cpp/src/arrow/compute/kernels/scalar_cast_test.cc index 364a4bd436b..f5005cb43ce 100644 --- a/cpp/src/arrow/compute/kernels/scalar_cast_test.cc +++ b/cpp/src/arrow/compute/kernels/scalar_cast_test.cc @@ -4573,6 +4573,19 @@ TEST(Cast, DictTypeToAnotherDict) { Cast(arr, dictionary(int8(), int8()), CastOptions::Safe())); } +TEST(Cast, EmptyDictionaryToAnotherDictionary) { + auto dictionary_values = ArrayFromJSON(utf8(), R"(["foo", "bar"])"); + + ASSERT_OK_AND_ASSIGN(auto input, DictionaryArray::FromArrays( + dictionary(int32(), utf8()), + ArrayFromJSON(int32(), "[]"), dictionary_values)); + ASSERT_OK_AND_ASSIGN(auto result, Cast(*input, dictionary(int64(), utf8()))); + auto dictionary_result = checked_pointer_cast(result); + + ASSERT_TRUE(dictionary_result->indices()->type()->Equals(int64())); + AssertArraysEqual(*dictionary_values, *dictionary_result->dictionary()); +} + TEST(Cast, NoOutBitmapIfInIsAllValid) { auto a = ArrayFromJSON(int8(), "[1]"); CastOptions options;