Skip to content

[C++] Acero produces unexpected output type for add function on decimals #35843

Description

@agoncharuk

Describe the bug, including details regarding any error messages, version, and platform.

According to compute functions documentation, the add function result precision and scale are computed as

scale = max(s1, s2)
precision = max(p1-s1, p2-s2) + 1 + scale

For some reason, this inference produces an incorrect result for decimal(10, 1) and decimal(10, 2) types when using acero streaming engine, namely:

add(decimal(10, 1), decimal(10, 2)) -> decimal(11, 1); // Unexpected
add(decimal(10, 2), decimal(10, 1)) -> decimal(12, 2); // Ok

Full test:

    auto t0 = std::static_pointer_cast<arrow::DecimalType>(*arrow::DecimalType::Make(arrow::Type::DECIMAL128, 10, 1));
    auto t1 = std::static_pointer_cast<arrow::DecimalType>(*arrow::DecimalType::Make(arrow::Type::DECIMAL128, 10, 2));

    arrow::Decimal128Builder b0(t0);
    arrow::Decimal128Builder b1(t1);

    for (int i = 0; i < 10; ++i) {
        EXPECT_TRUE(b0.Append(i * 10).ok());
        EXPECT_TRUE(b1.Append((i + 1) * 100).ok());
    }

    auto batch = arrow::compute::ExecBatch::Make({*b0.Finish(), *b1.Finish()}, 10).ValueOrDie();
    auto gen = arrow::MakeVectorGenerator(std::vector{std::make_optional(std::move(batch))});
    auto schema = arrow::schema({arrow::field("c0", t0), arrow::field("c1", t1)});

    arrow::acero::Declaration source{"source", arrow::acero::SourceNodeOptions{schema, std::move(gen)}};

    arrow::compute::Expression p0 = cp::call("add", {arrow::compute::field_ref("c0"), arrow::compute::field_ref("c1")});
    arrow::compute::Expression p1 = cp::call("add", {arrow::compute::field_ref("c1"), arrow::compute::field_ref("c0")});

    arrow::acero::Declaration project{
      "project", {std::move(source)}, arrow::acero::ProjectNodeOptions({p0, p1})};

    auto responseTable = arrow::acero::DeclarationToTable(std::move(project)).ValueOrDie();
    auto outSchema = responseTable->schema();

    auto scale = std::max(t0->scale(), t1->scale());
    auto precision = std::max(t0->precision() - t0->scale(), t1->precision() - t1->scale()) + 1 + scale;
    auto expectedOutType = *arrow::DecimalType::Make(arrow::Type::DECIMAL128, precision, scale);

    for (int c = 0; c < outSchema->num_fields(); ++c) {
        EXPECT_TRUE(expectedOutType->Equals(outSchema->field(c)->type())) << "Failed for " << c << ": " << outSchema->field(c)->type()->ToString();
    }

If I call arrow::compute::Add() directly on Datums, the expected types are correct regardless of the order.

Component(s)

C++

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions