vapor/fluent-mysql-driver

`.max()` (and possibly other aggregation functions) throw an error on empty table for MySQL

Cellane opened this issue · 4 comments

This is mostly a re-posting of issue vapor/fluent#541, since it seems the issue is related to MySQL-specific parts of Fluent, not Fluent itself. Here’s a minimal repro case:

// Todo.swift
import FluentMySQL
import Vapor

final class Todo: MySQLModel {
    var id: Int?
    var name: String
    var order: Int

    init(_ name: String, _ order: Int) {
        self.name = name
        self.order = order
    }
}

extension Todo: Migration {}
extension Todo: Content {}
extension Todo: Parameter {}
// TodoController.swift
import Vapor

final class TodoController {
    func createHandler(
        _ req: Request,
        todoData: CreateTodoRequest
    ) throws -> Future<Todo> {
        return Todo.query(on: req)
            .max(\Todo.order)
            .catchMap { _ in 0 }
            .flatMap { maxOrder in
                let todo = Todo(todoData.name, maxOrder + 1)

                return todo.save(on: req)
        }
    }
}

struct CreateTodoRequest: Content {
    let name: String
}
// routes.swift
import Vapor

public func routes(_ router: Router) throws {
    let todoController = TodoController()
    router.post(CreateTodoRequest.self, at: "todos", use: todoController.createHandler)
}

This code attempts to find maximum value of the order field and set the value of newly created object to be one higher.

However, when sending a {"name": "Test"} request to localhost:8080/todos, everything goes well until code reaches the return todo.save(on: req) line; at that point, crash is emitted from the MySQL/Connection/MySQLConnection.swift:82 file, along with this error message:

Assertion failed: Attempting to call `send(...)` again before previous invocation has completed.

Package versions: Vapor 3.0.6, Fluent 3.0.0-rc.4.0.2, FluentMySQL 3.0.0-rc.4.0.3, MySQL 3.0.0-rc.4.3. MySQL itself is Docker image tagged as mysql:5.

Forgot to mention: this really seems to only happen on an empty table (or possibly a table where MAX(order) would have no value to return?); in other words, if MAX(order) returns a non-null value, the code works correctly.

@Cellane I suspect this is related to an issue fixed in MySQL 3.0.0 (just released). Can you run swift package update and let me know if that resolves it?

@tanner0101 I’m afraid I’m still hitting the same issue. This time the line number is 81, but the code on the line is the same.

Vapor 3.0.6, Fluent 3.0.0, FluentMySQL 3.0.0, MySQL 3.0.0.

Could it be somehow related to vapor/database-kit#43, by the way? The crash reported in that issue seems to originate from the same line of code.