stephencelis / SQLite.swift

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

Error when using `decode` on a `pluck` result #1120

Open tylerbrandt opened 2 years ago

tylerbrandt commented 2 years ago

Issues are used to track bugs and feature requests. Need help or have a general question? Ask on Stack Overflow (tag sqlite.swift).

Build Information

SQLite.swift version: 0.13.2 (installed with Xcode "Add packages...") Xcode version: 13.2.1 macOS version: 12.3

Description of issue

When I try to decode a Codable type using db.prepare and iterating over the results, it works. However, when use db.pluck to fetch at most one of the same type, it fails with the error:

DecodingError
  ▿ dataCorrupted : Context
    - codingPath : 0 elements
    - debugDescription : "decoding a single value container is not supported"
    - underlyingError : nil

Playground code to reproduce the error above (I created as a new page in the SQLite.swift playground):

import SQLite

var db = try Connection(.inMemory)

struct User: Codable {
  let name: String
}

let id = Expression<Int64>("id")
let name = Expression<String>("name")

let userTable = Table("user")
try db.run(userTable.create() { tbl in
  tbl.column(id, primaryKey: true)
  tbl.column(name)
})

try db.run(userTable.insert(User(name: "User 1")))

let results = try db.pluck(userTable)
print(try results?.decode() as User?)

Looking at the source code for the string in question, it seems like this is triggering the SQLiteDecoder.singleValueContainer method, and the error is being thrown intentionally, but this limitation isn't noted in the docs so it's possible I'm doing something wrong.

lightandshadow68 commented 1 year ago

I ran into the same issue. It seems that Swift's Codable implementation defaults to that container type when it doesn't quite know what the generic type V is, despite not generating a complier error.

You can get Swift to generate a complier error like so....

let connection = try Database.getConnection()
let id = Expression<String>("id")
let windowTable = CaptureWindow.getTable()

do {
    if let row = try connection.pluck(windowTable.filter(id == windowID)) {
        let window = try row.decode()
        return window
    }
} catch {
    print(error.localizedDescription)
}

return nil

To resolve, I specified the type.

let window: CaptureWindow = try row.decode()

(Note: I'm working on a project that already had a number of Codable extensions, which might play a role in this issue. I had to comment some of them out to get SQLite.swift Codable working. You might want to check for extensions in your project as well.)

lightandshadow68 commented 11 months ago

Just ran into this again, even when specifying the type as indicated above. To resolve, I switched to...

let session = try row.decode() as CaptureSession