Skip to content

Commit 0ba651d

Browse files
committed
Basic subquery support
Signed-off-by: Stephen Celis <[email protected]>
1 parent 1efaf8b commit 0ba651d

File tree

3 files changed

+57
-5
lines changed

3 files changed

+57
-5
lines changed

SQLite Tests/QueryTests.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ class QueryTests: XCTestCase {
5454
ExpectExecutions(db, [SQL: 1]) { _ in for _ in query {} }
5555
}
5656

57+
func test_select_withSubquery() {
58+
let subquery = users.select(id)
59+
60+
var query = users.select(subquery)
61+
var SQL = "SELECT (SELECT \"id\" FROM \"users\") FROM \"users\""
62+
ExpectExecutions(db, [SQL: 1]) { _ in for _ in query {} }
63+
64+
query = users.select(subquery.alias("u"))
65+
SQL = "SELECT (SELECT \"id\" FROM (\"users\") AS \"u\") AS \"u\" FROM \"users\""
66+
ExpectExecutions(db, [SQL: 1]) { _ in for _ in query {} }
67+
}
68+
5769
func test_join_compilesJoinClause() {
5870
let managers = db["users"].alias("managers")
5971

@@ -117,6 +129,28 @@ class QueryTests: XCTestCase {
117129
ExpectExecutions(db, [SQL: 1]) { _ in for row in query { println(row) } }
118130
}
119131

132+
func test_join_withSubquery_joinsSubquery() {
133+
let maxId = max(id).alias("max_id")
134+
let subquery = users.select(maxId).group(age)
135+
let query = users.join(subquery, on: maxId == id)
136+
137+
let SQL = "SELECT * FROM \"users\" " +
138+
"INNER JOIN (SELECT (max(\"id\")) AS \"max_id\" FROM \"users\" GROUP BY \"age\") " +
139+
"ON (\"max_id\" = \"id\")"
140+
ExpectExecutions(db, [SQL: 1]) { _ in for row in query { println(row) } }
141+
}
142+
143+
func test_join_withAliasedSubquery_joinsSubquery() {
144+
let maxId = max(id).alias("max_id")
145+
let subquery = users.select(maxId).group(age).alias("u")
146+
let query = users.join(subquery, on: subquery[maxId] == id)
147+
148+
let SQL = "SELECT * FROM \"users\" " +
149+
"INNER JOIN (SELECT (max(\"id\")) AS \"max_id\" FROM (\"users\") AS \"u\" GROUP BY \"age\") AS \"u\" " +
150+
"ON (\"u\".\"max_id\" = \"id\")"
151+
ExpectExecutions(db, [SQL: 1]) { _ in for row in query { println(row) } }
152+
}
153+
120154
func test_namespacedColumnRowValueAccess() {
121155
let aliceId = users.insert(email <- "[email protected]")!
122156
let bettyId = users.insert(email <- "[email protected]", manager_id <- Int64(aliceId))!

SQLite/Expression.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ public struct Expression<T> {
8181
/// Returns an aliased version of the expression using AS. The expression is
8282
/// expanded contextually (e.g., in SELECT clauses).
8383
public func alias(alias: String) -> Expression {
84+
return self.alias(Expression(alias))
85+
}
86+
87+
private func alias(alias: Expression) -> Expression {
8488
var expression = Expression(alias)
8589
expression.original = self
8690
return expression
@@ -200,6 +204,17 @@ extension Expression: Expressible {
200204

201205
}
202206

207+
extension Query: Expressible {
208+
209+
public var expression: Expression<()> {
210+
if tableName.original != nil {
211+
return selectExpression.alias(tableName)
212+
}
213+
return wrap("", selectExpression)
214+
}
215+
216+
}
217+
203218
// MARK: - Expressions
204219

205220
public func + (lhs: Expression<String>, rhs: Expression<String>) -> Expression<String> { return infix("||", lhs, rhs) }

SQLite/Query.swift

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public struct Query {
5252

5353
}
5454

55-
private var columns: [Expressible] = [Expression<()>(literal: "*")]
55+
private var columns: [Expressible]?
5656
private var distinct: Bool = false
5757
internal var tableName: Expression<()>
5858
private var joins: [(type: JoinType, table: Query, condition: Expression<Bool>)] = []
@@ -96,7 +96,9 @@ public struct Query {
9696
///
9797
/// :returns: A query with SELECT * applied.
9898
public func select(all star: Star) -> Query {
99-
return select(star(nil, nil))
99+
var query = self
100+
(query.distinct, query.columns) = (false, nil)
101+
return query
100102
}
101103

102104
/// Sets the SELECT DISTINCT * clause on the query.
@@ -392,15 +394,16 @@ public struct Query {
392394
private var selectClause: Expressible {
393395
var expressions: [Expressible] = [Expression<()>(literal: "SELECT")]
394396
if distinct { expressions.append(Expression<()>(literal: "DISTINCT")) }
395-
expressions.append(Expression<()>.join(", ", columns.map { $0.expression.aliased }))
397+
expressions.append(Expression<()>.join(", ", (columns ?? [Expression<()>(literal: "*")]).map { $0.expression.aliased }))
396398
expressions.append(Expression<()>(literal: "FROM \(tableName.aliased.SQL)"))
397399
return Expression<()>.join(" ", expressions)
398400
}
399401

400402
private var joinClause: Expressible? {
401403
if joins.count == 0 { return nil }
402404
return Expression<()>.join(" ", joins.map { type, table, condition in
403-
Expression<()>(literal: "\(type.rawValue) JOIN \(table.tableName.aliased.SQL) ON \(condition.SQL)", condition.bindings)
405+
let join = (table.columns == nil ? table.tableName : table.expression).aliased
406+
return Expression<()>(literal: "\(type.rawValue) JOIN \(join.SQL) ON \(condition.SQL)", join.bindings + condition.bindings)
404407
})
405408
}
406409

@@ -824,7 +827,7 @@ public struct QueryGenerator: GeneratorType {
824827

825828
private lazy var columnNames: [String: Int] = {
826829
var (columnNames, idx) = ([String: Int](), 0)
827-
column: for each in self.query.columns {
830+
column: for each in self.query.columns ?? [Expression<()>(literal: "*")] {
828831
let pair = split(each.expression.SQL) { $0 == "." }
829832
let (tableName, column) = (pair.count > 1 ? pair.first : nil, pair.last!)
830833

0 commit comments

Comments
 (0)