diff --git a/cpp/src/arrow/scalar.cc b/cpp/src/arrow/scalar.cc index 85ceec97202..f9a56c79c9d 100644 --- a/cpp/src/arrow/scalar.cc +++ b/cpp/src/arrow/scalar.cc @@ -1177,14 +1177,16 @@ enable_if_duration>> CastImpl( } // time to time -template +template ::ScalarType::TypeClass> enable_if_time>> CastImpl( const TimeScalar& from, std::shared_ptr to_type) { using ToScalar = typename TypeTraits::ScalarType; ARROW_ASSIGN_OR_RAISE( auto value, util::ConvertTimestampValue(AsTimestampType(from.type), AsTimestampType(to_type), from.value)); - return std::make_shared(value, std::move(to_type)); + return std::make_shared(static_cast(value), + std::move(to_type)); } constexpr int64_t kMillisecondsInDay = 86400000; @@ -1288,10 +1290,11 @@ CastImpl(const StructScalar& from, std::shared_ptr to_type) { } // casts between variable-length and fixed-length list types -template +template std::enable_if_t::value && is_list_type::value, Result>> -CastImpl(const From& from, std::shared_ptr to_type) { +CastImpl(const FromScalar& from, std::shared_ptr to_type) { if constexpr (sizeof(typename To::offset_type) < sizeof(int64_t)) { if (from.value->length() > std::numeric_limits::max()) { return Status::Invalid(from.type->ToString(), " too large to cast to ", diff --git a/cpp/src/arrow/scalar_test.cc b/cpp/src/arrow/scalar_test.cc index d19d7f8a39e..6938bc0d887 100644 --- a/cpp/src/arrow/scalar_test.cc +++ b/cpp/src/arrow/scalar_test.cc @@ -36,6 +36,7 @@ #include "arrow/status.h" #include "arrow/testing/extension_type.h" #include "arrow/testing/gtest_util.h" +#include "arrow/testing/random.h" #include "arrow/testing/util.h" #include "arrow/type_traits.h" @@ -156,6 +157,50 @@ TEST(TestBooleanScalar, Cast) { } } +TEST(TestScalar, IdentityCast) { + random::RandomArrayGenerator gen(/*seed=*/42); + auto test_identity_cast_for_type = + [&gen](const std::shared_ptr& data_type) { + auto tmp_array = gen.ArrayOf(data_type, /*size=*/1, /*null_probability=*/0.0); + ARROW_SCOPED_TRACE("data type = ", data_type->ToString()); + ASSERT_OK_AND_ASSIGN(auto scalar, tmp_array->GetScalar(0)); + ASSERT_OK_AND_ASSIGN(auto casted_scalar, scalar->CastTo(data_type)); + ASSERT_TRUE(casted_scalar->Equals(*scalar)); + ASSERT_TRUE(scalar->Equals(*casted_scalar)); + }; + for (auto& type : PrimitiveTypes()) { + test_identity_cast_for_type(type); + } + for (auto& type : DurationTypes()) { + test_identity_cast_for_type(type); + } + for (auto& type : IntervalTypes()) { + test_identity_cast_for_type(type); + } + for (auto& type : { + arrow::fixed_size_list(arrow::int32(), 20), arrow::list(arrow::int32()), + arrow::large_list(arrow::int32()), + // TODO(GH-45430): CastTo for ListView is not implemented yet. + // arrow::list_view(arrow::int32()), arrow::large_list_view(arrow::int32()) + // TODO(GH-45431): CastTo for ComplexType is not implemented yet. + // arrow::map(arrow::binary(), arrow::int32()), + // struct_({field("float", arrow::float32())}), + }) { + test_identity_cast_for_type(type); + } + // TODO(GH-45429): CastTo for Decimal is not implemented yet. + /* + for (auto& type: { + arrow::decimal32(2, 2), + arrow::decimal64(4, 4), + arrow::decimal128(10, 10), + arrow::decimal128(20, 20), + }) { + test_identity_cast_for_type(type); + } + */ +} + template class TestNumericScalar : public ::testing::Test { public: @@ -866,6 +911,9 @@ TEST(TestTimeScalars, Basics) { ASSERT_TRUE(first->Equals(*MakeScalar(ty, 5).ValueOrDie())); ASSERT_TRUE(last->Equals(*MakeScalar(ty, 42).ValueOrDie())); ASSERT_FALSE(last->Equals(*MakeScalar("string"))); + + ASSERT_OK_AND_ASSIGN(auto casted, first->CastTo(ty)); + ASSERT_TRUE(casted->Equals(*first)); } }