Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance SQLLiteral and SQL interpolation #690

Merged
merged 25 commits into from
Feb 15, 2020
Merged

Conversation

groue
Copy link
Owner

@groue groue commented Jan 24, 2020

This pull requests helps injecting raw SQL snippets in the query interface.

The context of this pull request is that GRDB does not provide Swift APIs for all SQLite expressions. There is no built-in support for CAST, CASE, STRFTIME, etc. When one wants to use those constructs, one has to use raw SQL at some point, or submit a pull request which extends the query interface ;-).

One can always write full SQL queries, but those are not quite easy to compose, and their elements can't be reused:

// How to reuse the `date(createdAt)` snippet?
// How to reuse this request and refine it?
let day = "2020-01-23"
let request: SQLRequest<Player> = "SELECT * FROM player WHERE DATE(createdAt) = \(day)"

To solve this, we enhance SQL interpolation and the SQLLiteral type:

let day = "2020-01-23"
let createdAt = Column("createdAt")
let creationDay = SQLLiteral("DATE(\(createdAt))").sqlExpression

// SELECT * FROM "player" WHERE DATE("createdAt") = '2020-01-23'
let request = Player.filter(creationDay == day)

You can wrap this DATE function in a Swift function which accepts any value accepted by the database as a date (Date, String, etc.):

func date(_ value: SQLExpressible) -> SQLExpression {
    SQLLiteral("DATE(\(lhs.sqlExpression))").sqlExpression
}

let day = "2020-01-23"
let createdAt = Column("createdAt")

// SELECT * FROM "player" WHERE DATE("createdAt") = '2020-01-23'
let request = Player.filter(date(createdAt) == day)

If you follow the recommended practices for Record types, this can give:

extension Player {
    enum Columns {
        static let createdAt = Column(CodingKeys.createdAt)
    }
}

extension DerivableRequest where RowDecoder == Player {
    func filter(creationDay: DateComponents) -> Self {
        let dbDateComponents = DatabaseDateComponents(creationDay, format: .YMD)
        return filter(date(Player.Columns.createdAt) == dbDateComponents)
    }
}

// SELECT * FROM "player" WHERE DATE("createdAt") = '2020-01-23'
let day = DateComponents(year: 2020, month: 1, day: 23)
let request = Player.all().filter(creationDay: day)

@groue groue force-pushed the dev/qualifiedSQLLiteral branch from 77e4d3b to e4c6029 Compare January 25, 2020 13:37
@groue groue merged commit 3379bf3 into development Feb 15, 2020
@groue groue deleted the dev/qualifiedSQLLiteral branch February 15, 2020 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant