Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aggregate functions don't work with decimal columns #981

Open
ThatJoeMoore opened this issue Jul 3, 2024 · 1 comment
Open

Aggregate functions don't work with decimal columns #981

ThatJoeMoore opened this issue Jul 3, 2024 · 1 comment

Comments

@ThatJoeMoore
Copy link
Contributor

ThatJoeMoore commented Jul 3, 2024

Problem

The aggregateSum function doesn't work for values in a Decimal column.

Given a table like

value (Decimal)
1
2
3
4

Running

{
  _sum: {
    value: true
  }
}

Results in

{
  _sum: {
    value: "1234"
  }
}

Instead of

{
  _sum: {
    value: Decimal(10)
  }
}

Looking deeper, it looks like similar problems exist for avg too, and possibly for min and max (haven't tested those yet).

I can get the math to work properly if I pass a primitive number to Prismock when I insert data, but then the results of queries don't match the types that come out of Prisma.

Proposed Solution

If one or both of the inputs is a Decimal type, use Decimal.add to add them together. Something like:

if (accumulator instanceof Decimal || currentValue instanceof Decimal) {
  return new Decimal(accumulator).plus(new Decimal(currentValue))
}

This wraps the values in a new Decimal to make sure both sides are the correct type before adding.

This obviously requires updating the types around it to use number | Decimal.

The use of instanceof is illustrative; I'd consider having a more structural type check instead (specifically, you can check if it has fields of d: number[], e: number, and s: number. But that's probably just my inherent mistrust of instanceof in JS talking :) .

If we wanted to get more advanced, we could look at the types of the column in the model and see if it's a decimal type, in which case, we switch to a decimal-friendly version of the aggregator. The same approach could be extended for any other type-specific aggregation variants.

I can get a PR in to do the work, I'd just love guidance on how to implement the fix. The two big questions:

  • How do we check if we should use decimal arithmetic? Do we do it based on the type of the column in the model, or inline by detecting the type of the actual values?
  • If we do it inline, do we use instanceof (simple but can be broken by arcane dependency resolution issues) or a structural type check (less clear and is, at best, a really good guess at the type, but resilient against weird dependency issues)?

Personally, I think I'm leaning towards detecting if we have a 'special' column type and swapping out the implementation of the aggregators accordingly. Probably just for number and Decimal for now, but it could be expanded if Prisma adds aggregation support for other types too.

@morintd
Copy link
Owner

morintd commented Jul 6, 2024

Hello @ThatJoeMoore , thanks a lot for the detailed information!

I do agree with the mistrust of instanceof, and would prefer using the type of the column or as a last solution a structural check.

To get started, would you be able to add a test that reproduce this use-case? That would make our discussion easier, without having to do all the work at once.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants