SQLite.swift icon indicating copy to clipboard operation
SQLite.swift copied to clipboard

Aliased NOT EXISTS and an aliased column as part of SELECT

Open nestserau opened this issue 8 years ago • 1 comments

Is there a way to express the following query with the help of the library?

SELECT
  expo.id AS expo_id,
  (SELECT COUNT(*) FROM lead) lead_count,
  NOT EXISTS(SELECT * FROM lead WHERE result <> 1) leads_synced
FROM expo

If so, then could you give an example? After looking at the tests and skimming through the sources, I don't believe it's currently possible though. Maybe something for the future releases then.

nestserau avatar Feb 14 '17 11:02 nestserau

So for now I work around the issue using the following code. Maybe something to include into the library by default. If the repo owner confirms in the comments below that he approves of such an addition, I could prepare a pull request:

import Foundation
import SQLite

/// In case it's necessary to execute a SQL query that is currently not possible
/// to express in a type-safe manner using SQLite.swift library, prepare a raw
/// SQL statement and map the resulting rows to TypedRow, which will then cast
/// the column values to the user specified types.
/// 
/// Example code:
/// 
///     let statement = try db.prepare("SELECT expo.id AS expo_id FROM expo")
///     let rows = statement.typedRows()
///     let expos = rows.map { let id = $0.i64("expo_id"); return ... }
public final class TypedRow {
    
    private var columns: [String: Int]
    private var values: [Binding?]
    
    public init(columns theColumns: [String: Int], values theValues: [Binding?]) {
        
        columns = theColumns
        values = theValues
    }
    
    public convenience init(columns: [String], values: [Binding?]) {
        
        self.init(columns: columns.invertedIndexedDictionary, values: values)
    }
    
    private func value(for column: String) -> Binding? {
        return values[columns[column]!]
    }
    
    public func i64(_ column: String) -> Int64? {
        return value(for: column) as? Int64
    }
    
    public func int(_ column: String) -> Int? {
        return value(for: column) as? Int
    }
    
    public func u32(_ column: String) -> UInt32? {
        return value(for: column) as? UInt32
    }
    
    public func bool(_ column: String) -> Bool {
        return (value(for: column) as? Bool) ?? false
    }
    
    public func string(_ column: String) -> String? {
        return value(for: column) as? String
    }
    
    public func date(_ column: String) -> Date? {
        guard let rawValue = value(for: column) as? Int64 else { return nil }
        return rawValue.dateFromSQLiteTime()
    }
}

fileprivate extension Array where Element: Hashable  {
    
    var invertedIndexedDictionary: [Element: Int] {
        
        var result: [Element: Int] = [:]
        enumerated().forEach { result[$0.element] = $0.offset }
        return result
    }
}

public extension Statement {
    
    public func typedRows() -> [TypedRow] {
        return map { TypedRow(columns: columnNames,  values: $0) }
    }
}

nestserau avatar Feb 15 '17 09:02 nestserau