Skip to content

Commit fc0cf38

Browse files
committed
Support expression aliasing
This commit adds support for expression aliasing: let maxAge = max(age).alias("max_age") for row in users.select(name, maxAge).group(name) { println("the oldest \(row[name]) is \(row[maxAge]!)") // the oldest Stephen is 30 } // SELECT "name", (max("age")) AS "max_age" FROM "users" GROUP BY "name" Signed-off-by: Stephen Celis <[email protected]>
1 parent 74e80d3 commit fc0cf38

File tree

4 files changed

+35
-2
lines changed

4 files changed

+35
-2
lines changed

SQLite Tests/ExpressionTests.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ class ExpressionTests: XCTestCase {
2828
CreateUsersTable(db)
2929
}
3030

31+
func test_alias_aliasesExpression() {
32+
let aliased = stringA.alias("string_a")
33+
ExpectExecutionMatches("('A') AS \"string_a\"", aliased)
34+
}
35+
3136
func test_stringExpressionPlusStringExpression_buildsConcatenatingStringExpression() {
3237
ExpectExecutionMatches("('A' || 'A')", stringA + stringA)
3338
ExpectExecutionMatches("('A' || 'B')", stringA + stringB)

SQLite Tests/QueryTests.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,16 @@ class QueryTests: XCTestCase {
131131
XCTAssertEqual(alice[email], betty[managers[email]])
132132
}
133133

134+
func test_aliasedColumnRowValueAccess() {
135+
users.insert(email <- "[email protected]")!
136+
137+
let alias = email.alias("user_email")
138+
let query = users.select(alias)
139+
let alice = query.first!
140+
141+
XCTAssertEqual("[email protected]", alice[alias])
142+
}
143+
134144
func test_filter_compilesWhereClause() {
135145
let query = users.filter(admin == true)
136146

SQLite/Expression.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ public struct Expression<T> {
2929

3030
public let bindings: [Binding?]
3131

32-
internal var ascending: Bool?
32+
private var ascending: Bool?
33+
34+
private var original: Expressible?
3335

3436
/// Builds a SQL expression with a literal string and an optional list of
3537
/// bindings.
@@ -76,6 +78,14 @@ public struct Expression<T> {
7678
return expression
7779
}
7880

81+
/// Returns an aliased version of the expression using AS. The expression is
82+
/// expanded contextually (e.g., in SELECT clauses).
83+
public func alias(alias: String) -> Expression {
84+
var expression = Expression(alias)
85+
expression.original = self
86+
return expression
87+
}
88+
7989
internal static func join(separator: String, _ expressions: [Expressible]) -> Expression<()> {
8090
var (SQL, bindings) = ([String](), [Binding?]())
8191
for expressible in expressions {
@@ -89,6 +99,7 @@ public struct Expression<T> {
8999
internal init<V>(_ expression: Expression<V>) {
90100
self.init(literal: expression.SQL, expression.bindings)
91101
ascending = expression.ascending
102+
original = expression.original
92103
}
93104

94105
internal var ordered: Expression<()> {
@@ -98,6 +109,13 @@ public struct Expression<T> {
98109
return Expression<()>(self)
99110
}
100111

112+
internal var aliased: Expression {
113+
if let aliased = original?.expression {
114+
return Expression(literal: "(\(aliased.SQL)) AS \(SQL)", aliased.bindings + bindings)
115+
}
116+
return self
117+
}
118+
101119
internal func reverse() -> Expression {
102120
var expression = self
103121
expression.ascending = expression.ascending.map(!) ?? false

SQLite/Query.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ public struct Query {
389389
private var selectClause: Expressible {
390390
var expressions: [Expressible] = [Expression<()>(literal: "SELECT")]
391391
if distinct { expressions.append(Expression<()>(literal: "DISTINCT")) }
392-
expressions.append(Expression<()>.join(", ", columns))
392+
expressions.append(Expression<()>.join(", ", columns.map { $0.expression.aliased }))
393393
expressions.append(Expression<()>(literal: "FROM \(self)"))
394394
return Expression<()>.join(" ", expressions)
395395
}

0 commit comments

Comments
 (0)