vapor / fluent-postgres-driver

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

Conforming an `enum` to `ReflectionDecodable` makes Fluent create it as type `jsonb` instead of `int8` #79

Closed MrMage closed 6 years ago

MrMage commented 6 years ago

(I am using all relevant packages on the master branch.)

I have an enum like this:

public enum EntityType: SwiftProtobuf.Enum {
  public typealias RawValue = Int
  case unknown // = 0
  case appActivity // = 1
  // more cases
  case UNRECOGNIZED(Int)

  public init() {
    self = .unknown
  }

  public init?(rawValue: Int) {
    switch rawValue {
    case 0: self = .unknown
    case 1: self = .appActivity
    // more cases
    default: self = .UNRECOGNIZED(rawValue)
    }
  }

  public var rawValue: Int {
    switch self {
    case .unknown: return 0
    case .appActivity: return 1
    // more cases
    case .UNRECOGNIZED(let i): return i
    }
  }

}

If I try to make this codable and encoded as an int8 as follows, using EntityType fields in a model creates table columns of type int8 as expected:


extension EntityType: Codable, PostgreSQLDataConvertible, ReflectionDecodable {
    public static var postgreSQLDataType: PostgreSQLDataType { return .int8 }
    public static var postgreSQLDataArrayType: PostgreSQLDataType { return .array(.int8) }

    public init(from decoder: Swift.Decoder) throws {
        let container = try decoder.singleValueContainer()
        self.init(rawValue: try container.decode(Int.self))!
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(self.rawValue)
    }

    public static func convertFromPostgreSQLData(_ data: PostgreSQLData) throws -> EntityType {
        return EntityType(rawValue: try Int.convertFromPostgreSQLData(data))!
    }

    public func convertToPostgreSQLData() throws -> PostgreSQLData {
        return try self.rawValue.convertToPostgreSQLData()
    }
}

However, then I get runtime errors because the enum is not ReflectionDecodable. If I add ReflectionDecodable conformance like this:

extension EntityType: ReflectionDecodable {
    public static func reflectDecoded() throws -> (EntityType, EntityType) {
        return (.unknown, .appActivity)
    }
}

Then my migrations create the column as type jsonb instead and I get PostgreSQL errors like cannot compare jsonb = bigint for query filters like .filter(\MyModelType.entityType == .appActivity).

How can I ensure that Fluent creates my column of type .int8 even if conforming to ReflectionDecodable?

tanner0101 commented 6 years ago

@MrMage sorry this was a bit confusing since the PostgreSQLEnum type was changed to use native PSQL enums in a previous release. I've added back a PostgreSQLRawEnum which should give the behavior you are expecting.

Implementing an enum with this protocol looks something like:

enum Availability: UInt8, PostgreSQLRawEnum {
    static var allCases: [Availability] = [.everyday, .sunday, .monday, .tuesday, .wednesday, .thursday, .friday, .saturday]

    case everyday
    case sunday
    case monday
    case tuesday
    case wednesday
    case thursday
    case friday
    case saturday
}

struct Foo: PostgreSQLModel, Migration {
    var id: Int?
    var availability: Availability
}

In Swift 4.2, you will no longer need to implement allCases.

tanner0101 commented 6 years ago

Closing this now that we have PostgreSQLRawEnum. Let me know if that works for you 👍.

MrMage commented 6 years ago

Just a quick heads-up that that worked. Thanks, Tanner!