stephencelis / SQLite.swift

A type-safe, Swift-language layer over SQLite3.
MIT License
9.59k stars 1.55k forks source link

Add support for UUID type? #1037

Closed JetForMe closed 2 years ago

JetForMe commented 3 years ago

Build Information

General guidelines

Could we get support for UUID? I’m working on adding it, and it seemed that SQLite supported it natively, but I think Vapor just created my table with that as the type name. I'm trying to figure out what it really is underneath. Ah, it must be string. Pity, that's wasteful of space.

mlaster commented 2 years ago

I managed to add it to my local project easy enough with:

extension UUID: Value {
    public static var declaredDatatype: String {
        Blob.declaredDatatype
    }

    // swiftlint:disable:next force_unwrapping
    public static func fromDatatypeValue(_ dataValue: Blob) -> UUID { UUID(data: Data(dataValue.bytes))! }

    // swiftlint:disable:next force_unwrapping
    public var datatypeValue: Blob { data.withUnsafeBytes { Blob(bytes: $0.baseAddress!, length: data.count) } }
}

I can use it in an expression like any other type:

Expression("identifier")

I think I have a few more extensions to make this work:

public extension UUID {
    /// Construct a `UUID` from raw `Data`
    init?(data: Data) {
        guard data.count == 16 else {
            return nil
        }
        self = UUID(uuid: BytesToUUID(data.bytes))
    }

    /// Returns a `Data` representation of the UUID
    var data: Data {
        var raw = uuid
        return withUnsafeBytes(of: &raw) { Data($0) }
    }
}
// swiftlint:disable:next identifier_name
private func BytesToUUID( _ u: [UInt8] ) -> uuid_t {
    ( u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8], u[9], u[10], u[11], u[12], u[13], u[14], u[15])
}
nathanfallet commented 2 years ago

Should be fixed by #981. Tell me if there's something wrong.

mlaster commented 2 years ago

Why are UUID values stored as Strings instead of Blobs? It seems it would be more efficient to store as blobs since they are fixed length binary data. Doesn't string comparison involve extra Unicode overhead?

jberkel commented 2 years ago

In #981 it was decided to "Storing them as string for better readability in the database", i.e. it was optimized for human legibility.

Probably not a good call, given that you can always use hex(...) to turn them back into a string.

mlaster commented 2 years ago

Unfortunately the prevents me from storing it as a blob. I used to have a extension UUID: Value { } to handle UUID natively as a Blob. When I updated to a newer version of SQLite.swift it was in conflict with the new built-in one.

Human readability isn't as important as performance, especially since you can just run .mode quote in sqlite3 and make blobs completely readable.

JetForMe commented 2 years ago

Update on my op: Vapor is storing them as SQLite native UUID type, and the sqlite3 CLI renders them as a human-readable string.

jberkel commented 2 years ago

I agree that string storage is not a great default setting, it's really wasteful in terms of space. Ideally, the user of the library should be able to pick the correct serialization format required. Given the static nature of extensions in Swift, I'm not sure how feasible this is.

mlaster commented 2 years ago

There is also the whole issue of

"BB585245-7338-4638-8DF5-BF20B9EDD589" != "bb585245-7338-4638-8df5-bf20b9edd589"

Which would go away if they were stored as blobs.