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

Fluent Postgres driver crashes getting sum and average aggregates #570

Closed
hsharghi opened this issue Jun 21, 2023 · 1 comment
Closed

Fluent Postgres driver crashes getting sum and average aggregates #570

hsharghi opened this issue Jun 21, 2023 · 1 comment
Labels
bug Something isn't working

Comments

@hsharghi
Copy link

Describe the bug

In Postgres database, getting aggregate values of sum and average will throw PostgresDecodingError
(MySQL and SQLite databases both works as expected, and the bug is only occurred when using Postgres.)

PostgresDecodingError(code: typeMismatch, columnName: "aggregate", columnIndex: 0, targetType: Swift.Optional<Swift.Int>, postgresType: NUMERIC, postgresFormat: binary, postgresData: ByteBuffer { readerIndex: 0, writerIndex: 10, readableBytes: 10, capacity: 10, storageCapacity: 2048, slice: _ByteBufferSlice { 71..<81 }, storage: 0x0000000101827600 (2048 bytes) }

To Reproduce

Steps to reproduce the behavior:

  1. Create model and migration for a simple model containing an Integer column.
final class User: Model {       
    static let schema = "users"
    
    @ID(key: .id)
    var id: UUID?

    @Field(key: "balance")
    var balance: Int
    
    init() { }
    
    init(id: UUID? = nil, balance: Int) {
        self.id = id
        self.balance = balance
    }
}
  1. Create migrations for User model
struct CreateUsers: AsyncMigration {
    func prepare(on database: Database) async throws {
        try await database.schema(User.schema)
            .id()
            .field("balance", .integer)
            .create()
    }
    
    func revert(on database: Database) async throws {
        try await database.schema(User.schema).delete()
    }
}
  1. Add a method to get sum and average of balance for all users
extension User {
    func getSum(on db: Database) async throws -> Int {
       try await User.query(on: db)
            .sum(\.$balance)
    }

    func getAverage(on db: Database) async throws -> Int {
       try await User.query(on: db)
            .average(\.$balance)
    }
}
  1. Call any of getSum or getAverage methods will cause the app to crash.

Expected behavior

It is expected to get the sum or average values of the given column.

Environment

macOS Ventura 13.4 (22F66)
Swift 5.8
Vapor framework: 4.77.0
toolbox: 18.7.1

Additional context

As @gwynne mentioned in Discord:
The problem is caused by two factors:

  1. Postgres is - correctly - concerned that taking the sum of a given series of values of a given integer type could overflow that type (imagine, for example, trying to add any positive number to an integer with the value 2_147_483_647, the maximum possible value for that type). Postgres returns the result as the next-larger integer type, which in the case of bigint is numeric (because there are no larger purely-integer types). In the case of avg(), it's the rather more straightforward issue that the average of any given series of integers of any size is not itself necessarily an integer. Notably, MySQL and SQLite don't do this, or at least not in a way that trips over the second factor:

  2. Fluent does not take into account the fact that many aggregate functions do not return a value of the same type as their inputs. This is what really makes this ugly to fix; the fix would require breaking public API.

@hsharghi hsharghi added the bug Something isn't working label Jun 21, 2023
@gwynne
Copy link
Member

gwynne commented Sep 22, 2024

This is a duplicate of vapor/fluent-postgres-driver#166

@gwynne gwynne closed this as not planned Won't fix, can't repro, duplicate, stale Sep 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants