Getting results from arbitrary SQL statements with correct binding in SQLite.swift
Asked Answered
B

1

7

The SQLite.swift documentation says about executing arbitrary SQL:

let stmt = try db.prepare("SELECT id, email FROM users")
for row in stmt {
    for (index, name) in stmt.columnNames.enumerate() {
        print ("\(name)=\(row[index]!)")
        // id: Optional(1), email: Optional("[email protected]")
    }
}

I wanted to get the values directly like this

let stmt = try db.prepare("SELECT id, email FROM users")
for row in stmt {
    let myInt: Int64 = row[0] // error: Cannot convert value of type 'Binding?' to specified type 'Int64'
    let myString: String = row[1] // error: Cannot convert value of type 'Binding?' to specified type 'String'
}

but the row index is of type Binding? and I can't figure out how to convert that to the type I need. I see there is a Statement.bind method in the source code but I am still not discovering how to apply it.

Brightness answered 15/8, 2016 at 17:5 Comment(2)
Did you try using Expression?, (eg. let myInt: Expression<Int64> = ...)Deathblow
@l'L'l, Good idea. Unfortunately it gives the same error (can't convert Binding? to Expression<Int64>). I've been successful using Expression in the past but now that I am trying to get arbitrary SQL to work (because of my problem with this), I've been having a lot of trouble.Brightness
Q
8

You can retrieve correctly typed selected columns from a table like this:

// The database.
let db = try Connection(...)

// The table.
let users = Table("users")

// Typed column expressions.
let id = Expression<Int64>("id")
let email = Expression<String>("email")

// The query: "SELECT id, email FROM users"
for user in try db.prepare(users.select(id, email)) {
    let id = user[id]       // Int64
    let mail = user[email]  // String
    print(id, mail)
}

An alternative is to (optionally) cast the Binding values to the correct type:

let stmt = try db.prepare("SELECT id, email FROM users")
for row in stmt {
    if let id = row[0] as? Int64,
        let mail = row[1] as? String {
        print(id, mail)
    }
}
Quicktempered answered 15/8, 2016 at 18:35 Comment(3)
The second part of your answer solved my problem. The first part of your answer is surely better for the standard case (and I have done it like this before), but I am specifically wondering how to do it when executing arbitrary SQL. (I updated my question slightly to reflect that.) The reason I am resorting to arbitrary SQL is because of this as of yet unsolved problem.Brightness
Is there any way to bind the column names using Expression when accessing the row data after an arbitrary SQL query? I can see bugs coming in the future when I forget to update the hard coded column index numbers.Brightness
@Suragch: I doubt it. That would require to analyze the query string.Quicktempered

© 2022 - 2024 — McMap. All rights reserved.