diff --git a/cpp/src/arrow/compute/kernels/aggregate_basic.cc b/cpp/src/arrow/compute/kernels/aggregate_basic.cc index de8df32a04533..fe48b31b6b8a2 100644 --- a/cpp/src/arrow/compute/kernels/aggregate_basic.cc +++ b/cpp/src/arrow/compute/kernels/aggregate_basic.cc @@ -365,8 +365,10 @@ struct ProductImpl : public ScalarAggregator { } Status Finalize(KernelContext*, Datum* out) override { - std::shared_ptr out_type_; - ARROW_ASSIGN_OR_RAISE(out_type_, WidenDecimalToMaxPrecision(this->out_type)); + std::shared_ptr out_type_ = this->out_type; + if (is_decimal(this->out_type->id())) { + ARROW_ASSIGN_OR_RAISE(out_type_, WidenDecimalToMaxPrecision(this->out_type)); + } if ((!options.skip_nulls && this->nulls_observed) || (this->count < options.min_count)) { @@ -1051,6 +1053,10 @@ void RegisterScalarAggregateBasic(FunctionRegistry* registry) { func = std::make_shared("sum", Arity::Unary(), sum_doc, &default_scalar_aggregate_options); AddArrayScalarAggKernels(SumInit, {boolean()}, uint64(), func.get()); + AddAggKernel(KernelSignature::Make({Type::DECIMAL32}, MaxPrecisionDecimalType), + SumInit, func.get(), SimdLevel::NONE); + AddAggKernel(KernelSignature::Make({Type::DECIMAL64}, MaxPrecisionDecimalType), + SumInit, func.get(), SimdLevel::NONE); AddAggKernel(KernelSignature::Make({Type::DECIMAL128}, MaxPrecisionDecimalType), SumInit, func.get(), SimdLevel::NONE); AddAggKernel(KernelSignature::Make({Type::DECIMAL256}, MaxPrecisionDecimalType), @@ -1079,6 +1085,10 @@ void RegisterScalarAggregateBasic(FunctionRegistry* registry) { &default_scalar_aggregate_options); AddArrayScalarAggKernels(MeanInit, {boolean()}, float64(), func.get()); AddArrayScalarAggKernels(MeanInit, NumericTypes(), float64(), func.get()); + AddAggKernel(KernelSignature::Make({Type::DECIMAL32}, MaxPrecisionDecimalType), + MeanInit, func.get(), SimdLevel::NONE); + AddAggKernel(KernelSignature::Make({Type::DECIMAL64}, MaxPrecisionDecimalType), + MeanInit, func.get(), SimdLevel::NONE); AddAggKernel(KernelSignature::Make({Type::DECIMAL128}, MaxPrecisionDecimalType), MeanInit, func.get(), SimdLevel::NONE); AddAggKernel(KernelSignature::Make({Type::DECIMAL256}, MaxPrecisionDecimalType), @@ -1128,6 +1138,8 @@ void RegisterScalarAggregateBasic(FunctionRegistry* registry) { AddMinMaxKernels(MinMaxInitDefault, BaseBinaryTypes(), func.get()); AddMinMaxKernel(MinMaxInitDefault, Type::FIXED_SIZE_BINARY, func.get()); AddMinMaxKernel(MinMaxInitDefault, Type::INTERVAL_MONTHS, func.get()); + AddMinMaxKernel(MinMaxInitDefault, Type::DECIMAL32, func.get()); + AddMinMaxKernel(MinMaxInitDefault, Type::DECIMAL64, func.get()); AddMinMaxKernel(MinMaxInitDefault, Type::DECIMAL128, func.get()); AddMinMaxKernel(MinMaxInitDefault, Type::DECIMAL256, func.get()); // Add the SIMD variants for min max @@ -1163,6 +1175,10 @@ void RegisterScalarAggregateBasic(FunctionRegistry* registry) { AddArrayScalarAggKernels(ProductInit::Init, UnsignedIntTypes(), uint64(), func.get()); AddArrayScalarAggKernels(ProductInit::Init, FloatingPointTypes(), float64(), func.get()); + AddAggKernel(KernelSignature::Make({Type::DECIMAL32}, MaxPrecisionDecimalType), + ProductInit::Init, func.get(), SimdLevel::NONE); + AddAggKernel(KernelSignature::Make({Type::DECIMAL64}, MaxPrecisionDecimalType), + ProductInit::Init, func.get(), SimdLevel::NONE); AddAggKernel(KernelSignature::Make({Type::DECIMAL128}, MaxPrecisionDecimalType), ProductInit::Init, func.get(), SimdLevel::NONE); AddAggKernel(KernelSignature::Make({Type::DECIMAL256}, MaxPrecisionDecimalType), @@ -1188,7 +1204,7 @@ void RegisterScalarAggregateBasic(FunctionRegistry* registry) { AddBasicAggKernels(IndexInit::Init, PrimitiveTypes(), int64(), func.get()); AddBasicAggKernels(IndexInit::Init, TemporalTypes(), int64(), func.get()); AddBasicAggKernels(IndexInit::Init, - {fixed_size_binary(1), decimal128(1, 0), decimal256(1, 0), null()}, + {fixed_size_binary(1), decimal32(1, 0), decimal64(1, 0), decimal128(1, 0), decimal256(1, 0), null()}, int64(), func.get()); DCHECK_OK(registry->AddFunction(std::move(func))); } diff --git a/cpp/src/arrow/compute/kernels/aggregate_basic.inc.cc b/cpp/src/arrow/compute/kernels/aggregate_basic.inc.cc index 66d797d4dc177..b6781214e2df8 100644 --- a/cpp/src/arrow/compute/kernels/aggregate_basic.inc.cc +++ b/cpp/src/arrow/compute/kernels/aggregate_basic.inc.cc @@ -93,8 +93,10 @@ struct SumImpl : public ScalarAggregator { } Status Finalize(KernelContext*, Datum* out) override { - std::shared_ptr out_type_; - ARROW_ASSIGN_OR_RAISE(out_type_, WidenDecimalToMaxPrecision(this->out_type)); + std::shared_ptr out_type_ = this->out_type; + if (is_decimal(this->out_type->id())) { + ARROW_ASSIGN_OR_RAISE(out_type_, WidenDecimalToMaxPrecision(this->out_type)); + } if ((!options.skip_nulls && this->nulls_observed) || (this->count < options.min_count)) { diff --git a/cpp/src/arrow/compute/kernels/aggregate_test.cc b/cpp/src/arrow/compute/kernels/aggregate_test.cc index 65439af2748b5..f134d8edb8c53 100644 --- a/cpp/src/arrow/compute/kernels/aggregate_test.cc +++ b/cpp/src/arrow/compute/kernels/aggregate_test.cc @@ -494,51 +494,63 @@ TEST_F(TestSumKernelRoundOff, Basics) { } TEST(TestDecimalSumKernel, SimpleSum) { - for (const auto& ty : {decimal128(3, 2), decimal256(3, 2)}) { + std::vector> init_types = {decimal32(3, 2), decimal64(3, 2), decimal128(3, 2), decimal256(3, 2)}; + std::vector> out_types = {decimal32(9, 2), decimal64(18, 2), decimal128(38, 2), decimal256(76, 2)}; + + for (size_t i = 0; i < init_types.size(); ++i) { + auto& ty = init_types[i]; + auto& out_ty = out_types[i]; + EXPECT_THAT(Sum(ArrayFromJSON(ty, R"([])")), - ResultWith(ScalarFromJSON(ty, R"(null)"))); + ResultWith(ScalarFromJSON(out_ty, R"(null)"))); EXPECT_THAT(Sum(ArrayFromJSON(ty, R"([null])")), - ResultWith(ScalarFromJSON(ty, R"(null)"))); + ResultWith(ScalarFromJSON(out_ty, R"(null)"))); EXPECT_THAT( Sum(ArrayFromJSON(ty, R"(["0.00", "1.01", "2.02", "3.03", "4.04", "5.05"])")), - ResultWith(ScalarFromJSON(ty, R"("15.15")"))); + ResultWith(ScalarFromJSON(out_ty, R"("15.15")"))); Datum chunks = ChunkedArrayFromJSON(ty, {R"(["0.00", "1.01", "2.02", "3.03", "4.04", "5.05"])"}); - EXPECT_THAT(Sum(chunks), ResultWith(ScalarFromJSON(ty, R"("15.15")"))); + EXPECT_THAT(Sum(chunks), ResultWith(ScalarFromJSON(out_ty, R"("15.15")"))); chunks = ChunkedArrayFromJSON( ty, {R"(["0.00", "1.01", "2.02"])", R"(["3.03", "4.04", "5.05"])"}); - EXPECT_THAT(Sum(chunks), ResultWith(ScalarFromJSON(ty, R"("15.15")"))); + EXPECT_THAT(Sum(chunks), ResultWith(ScalarFromJSON(out_ty, R"("15.15")"))); chunks = ChunkedArrayFromJSON( ty, {R"(["0.00", "1.01", "2.02"])", "[]", R"(["3.03", "4.04", "5.05"])"}); - EXPECT_THAT(Sum(chunks), ResultWith(ScalarFromJSON(ty, R"("15.15")"))); + EXPECT_THAT(Sum(chunks), ResultWith(ScalarFromJSON(out_ty, R"("15.15")"))); ScalarAggregateOptions options(/*skip_nulls=*/true, /*min_count=*/0); EXPECT_THAT(Sum(ArrayFromJSON(ty, R"([])"), options), - ResultWith(ScalarFromJSON(ty, R"("0.00")"))); + ResultWith(ScalarFromJSON(out_ty, R"("0.00")"))); EXPECT_THAT(Sum(ArrayFromJSON(ty, R"([null])"), options), - ResultWith(ScalarFromJSON(ty, R"("0.00")"))); + ResultWith(ScalarFromJSON(out_ty, R"("0.00")"))); chunks = ChunkedArrayFromJSON(ty, {}); - EXPECT_THAT(Sum(chunks, options), ResultWith(ScalarFromJSON(ty, R"("0.00")"))); + EXPECT_THAT(Sum(chunks, options), ResultWith(ScalarFromJSON(out_ty, R"("0.00")"))); EXPECT_THAT( Sum(ArrayFromJSON(ty, R"(["1.01", null, "3.03", null, "5.05", null, "7.07"])"), options), - ResultWith(ScalarFromJSON(ty, R"("16.16")"))); + ResultWith(ScalarFromJSON(out_ty, R"("16.16")"))); EXPECT_THAT(Sum(ScalarFromJSON(ty, R"("5.05")")), - ResultWith(ScalarFromJSON(ty, R"("5.05")"))); + ResultWith(ScalarFromJSON(out_ty, R"("5.05")"))); EXPECT_THAT(Sum(ScalarFromJSON(ty, R"(null)")), - ResultWith(ScalarFromJSON(ty, R"(null)"))); + ResultWith(ScalarFromJSON(out_ty, R"(null)"))); EXPECT_THAT(Sum(ScalarFromJSON(ty, R"(null)"), options), - ResultWith(ScalarFromJSON(ty, R"("0.00")"))); + ResultWith(ScalarFromJSON(out_ty, R"("0.00")"))); } } TEST(TestDecimalSumKernel, ScalarAggregateOptions) { - for (const auto& ty : {decimal128(3, 2), decimal256(3, 2)}) { - Datum null = ScalarFromJSON(ty, R"(null)"); - Datum zero = ScalarFromJSON(ty, R"("0.00")"); - Datum result = ScalarFromJSON(ty, R"("14.14")"); + std::vector> init_types = {decimal32(3, 2), decimal64(3, 2), decimal128(3, 2), decimal256(3, 2)}; + std::vector> out_types = {decimal32(9, 2), decimal64(18, 2), decimal128(38, 2), decimal256(76, 2)}; + + for (size_t i = 0; i < init_types.size(); ++i) { + auto& ty = init_types[i]; + auto& out_ty = out_types[i ]; + + Datum null = ScalarFromJSON(out_ty, R"(null)"); + Datum zero = ScalarFromJSON(out_ty, R"("0.00")"); + Datum result = ScalarFromJSON(out_ty, R"("14.14")"); Datum arr = ArrayFromJSON(ty, R"(["1.01", null, "3.03", null, "3.03", null, "7.07"])"); @@ -578,7 +590,7 @@ TEST(TestDecimalSumKernel, ScalarAggregateOptions) { EXPECT_THAT(Sum(ScalarFromJSON(ty, R"("5.05")"), ScalarAggregateOptions(/*skip_nulls=*/false)), - ResultWith(ScalarFromJSON(ty, R"("5.05")"))); + ResultWith(ScalarFromJSON(out_ty, R"("5.05")"))); EXPECT_THAT(Sum(ScalarFromJSON(ty, R"("5.05")"), ScalarAggregateOptions(/*skip_nulls=*/true, /*min_count=*/2)), ResultWith(null)); @@ -711,49 +723,61 @@ TYPED_TEST(TestNumericProductKernel, ScalarAggregateOptions) { } TEST(TestDecimalProductKernel, SimpleProduct) { - for (const auto& ty : {decimal128(3, 2), decimal256(3, 2)}) { - Datum null = ScalarFromJSON(ty, R"(null)"); + std::vector> init_types = {decimal32(3, 2), decimal64(3, 2), decimal128(3, 2), decimal256(3, 2)}; + std::vector> out_types = {decimal32(9, 2), decimal64(18, 2), decimal128(38, 2), decimal256(76, 2)}; + + for (size_t i = 0; i < init_types.size(); ++i) { + auto& ty = init_types[i]; + auto& out_ty = out_types[i ]; + + Datum null = ScalarFromJSON(out_ty, R"(null)"); EXPECT_THAT(Product(ArrayFromJSON(ty, R"([])")), ResultWith(null)); EXPECT_THAT(Product(ArrayFromJSON(ty, R"([null])")), ResultWith(null)); EXPECT_THAT( Product(ArrayFromJSON(ty, R"(["0.00", "1.00", "2.00", "3.00", "4.00", "5.00"])")), - ResultWith(ScalarFromJSON(ty, R"("0.00")"))); + ResultWith(ScalarFromJSON(out_ty, R"("0.00")"))); Datum chunks = ChunkedArrayFromJSON(ty, {R"(["1.00", "2.00", "3.00", "4.00", "5.00"])"}); - EXPECT_THAT(Product(chunks), ResultWith(ScalarFromJSON(ty, R"("120.00")"))); + EXPECT_THAT(Product(chunks), ResultWith(ScalarFromJSON(out_ty, R"("120.00")"))); chunks = ChunkedArrayFromJSON(ty, {R"(["1.00", "2.00"])", R"(["-3.00", "4.00", "5.00"])"}); - EXPECT_THAT(Product(chunks), ResultWith(ScalarFromJSON(ty, R"("-120.00")"))); + EXPECT_THAT(Product(chunks), ResultWith(ScalarFromJSON(out_ty, R"("-120.00")"))); chunks = ChunkedArrayFromJSON( ty, {R"(["1.00", "2.00"])", R"([])", R"(["-3.00", "4.00", "-5.00"])"}); - EXPECT_THAT(Product(chunks), ResultWith(ScalarFromJSON(ty, R"("120.00")"))); + EXPECT_THAT(Product(chunks), ResultWith(ScalarFromJSON(out_ty, R"("120.00")"))); const ScalarAggregateOptions options(/*skip_nulls=*/true, /*min_count=*/0); EXPECT_THAT(Product(ArrayFromJSON(ty, R"([])"), options), - ResultWith(ScalarFromJSON(ty, R"("1.00")"))); + ResultWith(ScalarFromJSON(out_ty, R"("1.00")"))); EXPECT_THAT(Product(ArrayFromJSON(ty, R"([null])"), options), - ResultWith(ScalarFromJSON(ty, R"("1.00")"))); + ResultWith(ScalarFromJSON(out_ty, R"("1.00")"))); chunks = ChunkedArrayFromJSON(ty, {}); - EXPECT_THAT(Product(chunks, options), ResultWith(ScalarFromJSON(ty, R"("1.00")"))); + EXPECT_THAT(Product(chunks, options), ResultWith(ScalarFromJSON(out_ty, R"("1.00")"))); EXPECT_THAT(Product(ArrayFromJSON( ty, R"(["1.00", null, "-3.00", null, "3.00", null, "7.00"])"), options), - ResultWith(ScalarFromJSON(ty, R"("-63.00")"))); + ResultWith(ScalarFromJSON(out_ty, R"("-63.00")"))); EXPECT_THAT(Product(ScalarFromJSON(ty, R"("5.00")")), - ResultWith(ScalarFromJSON(ty, R"("5.00")"))); + ResultWith(ScalarFromJSON(out_ty, R"("5.00")"))); EXPECT_THAT(Product(null), ResultWith(null)); } } TEST(TestDecimalProductKernel, ScalarAggregateOptions) { - for (const auto& ty : {decimal128(3, 2), decimal256(3, 2)}) { - Datum null = ScalarFromJSON(ty, R"(null)"); - Datum one = ScalarFromJSON(ty, R"("1.00")"); - Datum result = ScalarFromJSON(ty, R"("63.00")"); + std::vector> init_types = {decimal32(3, 2), decimal64(3, 2), decimal128(3, 2), decimal256(3, 2)}; + std::vector> out_types = {decimal32(9, 2), decimal64(18, 2), decimal128(38, 2), decimal256(76, 2)}; + + for (size_t i = 0; i < init_types.size(); ++i) { + auto& ty = init_types[i]; + auto& out_ty = out_types[i ]; + + Datum null = ScalarFromJSON(out_ty, R"(null)"); + Datum one = ScalarFromJSON(out_ty, R"("1.00")"); + Datum result = ScalarFromJSON(out_ty, R"("63.00")"); Datum empty = ArrayFromJSON(ty, R"([])"); Datum null_arr = ArrayFromJSON(ty, R"([null])"); @@ -805,7 +829,7 @@ TEST(TestDecimalProductKernel, ScalarAggregateOptions) { EXPECT_THAT(Product(ScalarFromJSON(ty, R"("5.00")"), ScalarAggregateOptions(/*skip_nulls=*/false)), - ResultWith(ScalarFromJSON(ty, R"("5.00")"))); + ResultWith(ScalarFromJSON(out_ty, R"("5.00")"))); EXPECT_THAT(Product(ScalarFromJSON(ty, R"("5.00")"), ScalarAggregateOptions(/*skip_nulls=*/true, /*min_count=*/2)), ResultWith(null)); @@ -1336,93 +1360,118 @@ TYPED_TEST(TestRandomNumericMeanKernel, RandomArrayMeanOverflow) { TEST(TestDecimalMeanKernel, SimpleMean) { ScalarAggregateOptions options(/*skip_nulls=*/true, /*min_count=*/0); - for (const auto& ty : {decimal128(3, 2), decimal256(3, 2)}) { + std::vector> init_types = {decimal32(3, 2), decimal64(3, 2), decimal128(3, 2), decimal256(3, 2)}; + std::vector> out_types = {decimal32(9, 2), decimal64(18, 2), decimal128(38, 2), decimal256(76, 2)}; + + for (size_t i = 0; i < init_types.size(); ++i) { + auto& ty = init_types[i]; + auto& out_ty = out_types[i]; + // Decimal doesn't have NaN EXPECT_THAT(Mean(ArrayFromJSON(ty, R"([])"), options), - ResultWith(ScalarFromJSON(ty, R"(null)"))); + ResultWith(ScalarFromJSON(out_ty, R"(null)"))); EXPECT_THAT(Mean(ArrayFromJSON(ty, R"([null])"), options), - ResultWith(ScalarFromJSON(ty, R"(null)"))); + ResultWith(ScalarFromJSON(out_ty, R"(null)"))); EXPECT_THAT(Mean(ArrayFromJSON(ty, R"([])")), - ResultWith(ScalarFromJSON(ty, R"(null)"))); + ResultWith(ScalarFromJSON(out_ty, R"(null)"))); EXPECT_THAT(Mean(ArrayFromJSON(ty, R"([null])")), - ResultWith(ScalarFromJSON(ty, R"(null)"))); + ResultWith(ScalarFromJSON(out_ty, R"(null)"))); EXPECT_THAT(Mean(ArrayFromJSON(ty, R"(["1.01", null, "1.01"])")), - ResultWith(ScalarFromJSON(ty, R"("1.01")"))); + ResultWith(ScalarFromJSON(out_ty, R"("1.01")"))); // Check rounding EXPECT_THAT( Mean(ArrayFromJSON( ty, R"(["1.01", "2.02", "3.03", "4.04", "5.05", "6.06", "7.07", "8.08"])")), // 4.545 unrounded - ResultWith(ScalarFromJSON(ty, R"("4.55")"))); + ResultWith(ScalarFromJSON(out_ty, R"("4.55")"))); EXPECT_THAT( Mean(ArrayFromJSON( ty, R"(["-1.01", "-2.02", "-3.03", "-4.04", "-5.05", "-6.06", "-7.07", "-8.08"])")), // -4.545 unrounded - ResultWith(ScalarFromJSON(ty, R"("-4.55")"))); + ResultWith(ScalarFromJSON(out_ty, R"("-4.55")"))); EXPECT_THAT( Mean(ArrayFromJSON( ty, R"(["1.01", "2.02", "3.00", "4.04", "5.05", "6.06", "7.07", "8.08"])")), // 4.54125 unrounded - ResultWith(ScalarFromJSON(ty, R"("4.54")"))); + ResultWith(ScalarFromJSON(out_ty, R"("4.54")"))); EXPECT_THAT( Mean(ArrayFromJSON( ty, R"(["-1.01", "-2.02", "-3.00", "-4.04", "-5.05", "-6.06", "-7.07", "-8.08"])")), // -4.54125 unrounded - ResultWith(ScalarFromJSON(ty, R"("-4.54")"))); + ResultWith(ScalarFromJSON(out_ty, R"("-4.54")"))); EXPECT_THAT( Mean(ArrayFromJSON( ty, R"(["0.00", "0.00", "0.00", "0.00", "0.00", "0.00", "0.00", "0.00"])")), - ResultWith(ScalarFromJSON(ty, R"("0.00")"))); + ResultWith(ScalarFromJSON(out_ty, R"("0.00")"))); EXPECT_THAT( Mean(ArrayFromJSON( ty, R"(["1.01", "1.01", "1.01", "1.01", "1.01", "1.01", "1.01", "1.01"])")), - ResultWith(ScalarFromJSON(ty, R"("1.01")"))); + ResultWith(ScalarFromJSON(out_ty, R"("1.01")"))); EXPECT_THAT(Mean(ScalarFromJSON(ty, R"("5.05")")), - ResultWith(ScalarFromJSON(ty, R"("5.05")"))); + ResultWith(ScalarFromJSON(out_ty, R"("5.05")"))); EXPECT_THAT(Mean(ScalarFromJSON(ty, R"(null)")), - ResultWith(ScalarFromJSON(ty, R"(null)"))); + ResultWith(ScalarFromJSON(out_ty, R"(null)"))); } - for (const auto& ty : {decimal128(3, -2), decimal256(3, -2)}) { + // TODO: Currently, casts are not implemented for decimal32/64 so we ignore that for now + // init_types = {decimal32(3, -2), decimal64(3, -2), decimal128(3, -2), decimal256(3, -2)}; + // out_types = {decimal32(9, -2), decimal64(18, -2), decimal128(38, -2), decimal256(76, -2)}; + + init_types = {decimal128(3, -2), decimal256(3, -2)}; + out_types = {decimal128(38, -2), decimal256(76, -2)}; + + for (size_t i = 0; i < init_types.size(); ++i) { + auto& ty = init_types[i]; + auto& out_ty = out_types[i]; + // Check rounding + // + // N.B. In what follows, the additional Cast is due to the implementation of + // DecimalScalarFromJSON, which will try to construct a decimal with too big precision EXPECT_THAT( Mean(DecimalArrayFromJSON( ty, R"(["101E2", "202E2", "303E2", "404E2", "505E2", "606E2", "707E2", "808E2"])")), // 45450 unrounded - ResultWith(DecimalScalarFromJSON(ty, R"("455E2")"))); + ResultWith(Cast(DecimalScalarFromJSON(ty, R"("455E2")"), out_ty))); EXPECT_THAT( Mean(DecimalArrayFromJSON( ty, R"(["-101E2", "-202E2", "-303E2", "-404E2", "-505E2", "-606E2", "-707E2", "-808E2"])")), // -45450 unrounded - ResultWith(DecimalScalarFromJSON(ty, R"("-455E2")"))); + ResultWith(Cast(DecimalScalarFromJSON(ty, R"("-455E2")"), out_ty))); EXPECT_THAT( Mean(DecimalArrayFromJSON( ty, R"(["101E2", "202E2", "300E2", "404E2", "505E2", "606E2", "707E2", "808E2"])")), // 45412.5 unrounded - ResultWith(DecimalScalarFromJSON(ty, R"("454E2")"))); + ResultWith(Cast(DecimalScalarFromJSON(ty, R"("454E2")"), out_ty))); EXPECT_THAT( Mean(DecimalArrayFromJSON( ty, R"(["-101E2", "-202E2", "-300E2", "-404E2", "-505E2", "-606E2", "-707E2", "-808E2"])")), // -45412.5 unrounded - ResultWith(DecimalScalarFromJSON(ty, R"("-454E2")"))); + ResultWith(Cast(DecimalScalarFromJSON(ty, R"("-454E2")"), out_ty))); } } TEST(TestDecimalMeanKernel, ScalarAggregateOptions) { - for (const auto& ty : {decimal128(3, 2), decimal256(3, 2)}) { - Datum result = ScalarFromJSON(ty, R"("3.03")"); - Datum null = ScalarFromJSON(ty, R"(null)"); + std::vector> init_types = {decimal32(3, 2), decimal64(3, 2), decimal128(3, 2), decimal256(3, 2)}; + std::vector> out_types = {decimal32(9, 2), decimal64(18, 2), decimal128(38, 2), decimal256(76, 2)}; + + for (size_t i = 0; i < init_types.size(); ++i) { + auto& ty = init_types[i]; + auto& out_ty = out_types[i]; + + Datum result = ScalarFromJSON(out_ty, R"("3.03")"); + Datum null = ScalarFromJSON(out_ty, R"(null)"); Datum arr = ArrayFromJSON(ty, R"(["1.01", null, "2.02", "2.02", null, "7.07"])"); EXPECT_THAT(Mean(ArrayFromJSON(ty, "[]"), @@ -1480,8 +1529,8 @@ TEST(TestDecimalMeanKernel, ScalarAggregateOptions) { EXPECT_THAT(Mean(ScalarFromJSON(ty, R"("5.05")"), ScalarAggregateOptions(/*skip_nulls=*/false)), - ResultWith(ScalarFromJSON(ty, R"("5.05")"))); - EXPECT_THAT(Mean(ScalarFromJSON(ty, R"("5.05")"), + ResultWith(ScalarFromJSON(out_ty, R"("5.05")"))); + EXPECT_THAT(Mean(ScalarFromJSON(out_ty, R"("5.05")"), ScalarAggregateOptions(/*skip_nulls=*/true, /*min_count=*/2)), ResultWith(null)); EXPECT_THAT(Mean(null, ScalarAggregateOptions(/*skip_nulls=*/false)), diff --git a/cpp/src/arrow/compute/kernels/codegen_internal.cc b/cpp/src/arrow/compute/kernels/codegen_internal.cc index 7ced1d1e11a19..cbb875a1038fe 100644 --- a/cpp/src/arrow/compute/kernels/codegen_internal.cc +++ b/cpp/src/arrow/compute/kernels/codegen_internal.cc @@ -534,14 +534,22 @@ Status CastDecimalArgs(TypeHolder* begin, size_t count) { } Result> WidenDecimalToMaxPrecision(std::shared_ptr type) { - if (type->id() == Type::DECIMAL128) { - auto cast_type = checked_pointer_cast(type); - return Decimal128Type::Make(Decimal128Type::kMaxPrecision, cast_type->scale()); - } else if (type->id() == Type::DECIMAL256) { - auto cast_type = checked_pointer_cast(type); - return Decimal256Type::Make(Decimal256Type::kMaxPrecision, cast_type->scale()); - } - return type; + if (!is_decimal(type->id())) { + return Status::TypeError("Non-DecimalType passed to WidenDecimalToMaxPrecision: " + type->ToString()); + } + auto cast_type = checked_pointer_cast(type); + switch (type->id()) { + case Type::DECIMAL32: + return Decimal32Type::Make(Decimal32Type::kMaxPrecision, cast_type->scale()); + case Type::DECIMAL64: + return Decimal64Type::Make(Decimal64Type::kMaxPrecision, cast_type->scale()); + case Type::DECIMAL128: + return Decimal128Type::Make(Decimal128Type::kMaxPrecision, cast_type->scale()); + case Type::DECIMAL256: + return Decimal256Type::Make(Decimal256Type::kMaxPrecision, cast_type->scale()); + default: + return Status::TypeError("An unknown DecimalType was passed to WidenDecimalToMaxPrecision: " + type->ToString()); + }; } bool HasDecimal(const std::vector& types) { diff --git a/cpp/src/arrow/compute/kernels/test_util.h b/cpp/src/arrow/compute/kernels/test_util.h index 11e77caeff861..3bcf5d38f30b9 100644 --- a/cpp/src/arrow/compute/kernels/test_util.h +++ b/cpp/src/arrow/compute/kernels/test_util.h @@ -78,7 +78,11 @@ inline std::shared_ptr DecimalArrayFromJSON(const std::shared_ptr(*type); if (ty.scale() >= 0) return ArrayFromJSON(type, json); auto p = ty.precision() - ty.scale(); - auto adjusted_ty = ty.id() == Type::DECIMAL128 ? decimal128(p, 0) : decimal256(p, 0); + std::shared_ptr adjusted_ty = + ty.id() == Type::DECIMAL32 ? decimal32(p, 0) : + ty.id() == Type::DECIMAL64 ? decimal64(p, 0) : + ty.id() == Type::DECIMAL128 ? decimal128(p, 0) : + decimal256(p, 0); return Cast(ArrayFromJSON(adjusted_ty, json), type).ValueOrDie().make_array(); } @@ -87,7 +91,11 @@ inline std::shared_ptr DecimalScalarFromJSON( const auto& ty = checked_cast(*type); if (ty.scale() >= 0) return ScalarFromJSON(type, json); auto p = ty.precision() - ty.scale(); - auto adjusted_ty = ty.id() == Type::DECIMAL128 ? decimal128(p, 0) : decimal256(p, 0); + std::shared_ptr adjusted_ty = + ty.id() == Type::DECIMAL32 ? decimal32(p, 0) : + ty.id() == Type::DECIMAL64 ? decimal64(p, 0) : + ty.id() == Type::DECIMAL128 ? decimal128(p, 0) : + decimal256(p, 0); return Cast(ScalarFromJSON(adjusted_ty, json), type).ValueOrDie().scalar(); }