vapor / fluent-postgres-driver

🐘 PostgreSQL driver for Fluent.
MIT License
146 stars 53 forks source link

Model `entity` not equal to `sqlTableIdentifierString` #75

Closed geeksweep closed 4 years ago

geeksweep commented 6 years ago
struct Account: Content, Model, PostgreSQLTable {
    typealias Database = PostgreSQLDatabase
    typealias ID = Int
    static var idKey : WritableKeyPath<Account, Int?> = \Account.accountid
    static let entity = "account"

    var accountid:Int?
    var info:[accountinfo]
    var paid:Bool

}

extension Account{
    struct accountinfo : Content{
        var emailaddress:String
        var udid:String
        var udidtype:Int
        var pin:Int
    }
}
func boot(router: Router) throws {
        let accountRouter = router.grouped("account")
        accountRouter.get("quicky", use:quickTest)

func quickTest(_ req: Request) throws -> Future<[Account]> {
        return Account.query(on: req).filter(\Account.paid == true).all()
    }

request in browser:
http://localhost:8080/account/quicky

errorLog in the console:

Server starting on http://localhost:8080
[psql] [2018-07-01 02:11:14 +0000] SELECT * FROM "account" WHERE "Account"."paid" = $1 
[0x01 (BOOLEAN)] [ ERROR ] PostgreSQLError.server.error.errorMissingRTE: 
missing FROM-clause entry for table "Account" (Logger+LogError.swift:17)
[psql] [2018-07-01 02:13:01 +0000] SELECT * FROM "account" []

notice that the FROM is lower case "account" and the Where clause is uppercase 
"Account". dont know if this is the issue or not or provides some sort of hint as to 
what's going on. 

Notes: 
- that the database is already and existing database created manually not by any migration. 
- using Postgres v. 10.4 on localhost , Vapor 3.0.0+
- this works with just PostgreSQLModel but then i'm asked to conform by using an Int? 
which doesn't work for me in one of my tables that doesn't have a primary key so the 
concept of an 'id' isn't needed. 
geeksweep commented 6 years ago

@tanner0101 @0xTim or whoever else. please hit me up on discord if you want me to run tests real time to help debug whats going on. I'll be around.

mixio commented 6 years ago

I confirm getting the same error with the suggested test code and a fully qualified KeyPath:

return Account.query(on: req).filter(\Account.paid == true).all() // Qualified KeyPath.

[ ERROR ] PostgreSQLError.server.error.errorMissingRTE: missing FROM-clause entry for table "Account" (Logger+LogError.swift:17)

But with partial keypaths:

return Account.query(on: req).filter(\.paid, .equal, true).all() // Partial KeyPath.

or:

return Account.query(on: req).filter(\.paid == true).all() // Partial KeyPath.

It works seamlessly.

WORKAROUND:

Set static var sqlTableIdentifierString in the Account struct :

struct Account: Content, Model, PostgreSQLTable {
    typealias Database = PostgreSQLDatabase
    typealias ID = Int
    static var idKey : WritableKeyPath<Account, Int?> = \Account.accountid
    static let entity = "account"

   // ADD THIS PROPERTY TO RESOLVE ISSUE.
    static var sqlTableIdentifierString = entity // Which is "account" (lower case).

    var accountid:Int?
    var info:[accountinfo]
    var paid:Bool
}

allows to use the qualified form of the KeyPath.

PROBABLE CAUSE:

Apparently when using a fully qualified keypath in a predicate, the table name of a PostgreSQLTable is derived from protocol SQLTable defined in SQL 2.0.0-beta3/SQL/SQLTable.swift:

import Core

public protocol SQLTable: Codable, Reflectable {
    static var sqlTableIdentifierString: String { get }
}

extension SQLTable {
    public static var sqlTableIdentifierString: String {
        return "\(Self.self)"
    }
}

where the table name is set to the Type name. Hence the capitalized table name.

tanner0101 commented 6 years ago

@mixio's workaround is correct. I think we will need to reconcile the difference between sqlTableIdentifierString and entity in the next version of this package. I can't think of a good way to do this in a non-breaking way.