Skip to content

Commit a7b8a73

Browse files
committed
Cleanup
After failed experiment: SELECT ... FROM parent JOIN (SELECT * FROM child LIMIT 1) child ON child.parentId = child.id The problem is that the join condition can't go inside the subquery, because SQLite complains about unknown table "parent". And when the join condition is outside the subquery, the LIMIT clause has been applied too early. So we're still stuck with: SELECT ... FROM parent JOIN child ON child.id = (SELECT id FROM child WHERE child.parentId = child.id LIMIT 1) For some reason SQLite accepts the reference to the parent table in this case.
1 parent 278c18a commit a7b8a73

File tree

2 files changed

+37
-22
lines changed

2 files changed

+37
-22
lines changed

GRDB/QueryInterface/SQL/SQLRelation.swift

+11-4
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,15 @@ extension SQLRelation: Refinable {
224224
}
225225
}
226226

227+
// Prepending filters for aesthetic reasons
228+
func prependingFilters(_ filters: [SQLExpression]) -> Self {
229+
map(\.filtersPromise) { filtersPromise in
230+
filtersPromise.flatMap { existingFilters in
231+
DatabasePromise { _ in filters + existingFilters }
232+
}
233+
}
234+
}
235+
227236
func unfiltered() -> Self {
228237
with(\.filtersPromise, DatabasePromise(value: []))
229238
}
@@ -577,13 +586,11 @@ struct SQLAssociationCondition: Equatable {
577586
/// - parameter db: A database connection.
578587
/// - parameter leftAlias: A TableAlias for the table on the left of the
579588
/// JOIN operator.
580-
/// - parameter rightAlias: A TableAlias for the table on the right of the
581-
/// JOIN operator.
582589
/// - Returns: An array of SQL expression that should be joined with
583590
/// the AND operator.
584-
func expressions(_ db: Database, leftAlias: TableAlias, rightAlias: TableAlias) throws -> [SQLExpression] {
591+
func expressions(_ db: Database, leftAlias: TableAlias) throws -> [SQLExpression] {
585592
try columnMappings(db).map {
586-
QualifiedColumn($0.right, alias: rightAlias) == QualifiedColumn($0.left, alias: leftAlias)
593+
Column($0.right) == QualifiedColumn($0.left, alias: leftAlias)
587594
}
588595
}
589596

GRDB/QueryInterface/SQLGeneration/SQLQueryGenerator.swift

+26-18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
/// SQLQueryGenerator is able to generate an SQL SELECT query.
32
struct SQLQueryGenerator: Refinable {
43
fileprivate private(set) var relation: SQLQualifiedRelation
@@ -627,7 +626,7 @@ private struct SQLQualifiedJoin: Refinable {
627626

628627
enum Target {
629628
case all
630-
case subRelation(SQLRelation)
629+
case first(SQLRelation)
631630
}
632631

633632
var kind: Kind
@@ -667,10 +666,8 @@ private struct SQLQualifiedJoin: Refinable {
667666
.unfiltered()
668667
.unordered()
669668

670-
let subRelation = relation
671-
672669
self.relation = SQLQualifiedRelation(outerRelation)
673-
self.target = .subRelation(subRelation)
670+
self.target = .first(relation)
674671
} else {
675672
self.relation = SQLQualifiedRelation(relation)
676673
self.target = .all
@@ -709,32 +706,43 @@ private struct SQLQualifiedJoin: Refinable {
709706
case .leftJoin:
710707
allowsInnerJoin = false
711708
}
709+
710+
// JOIN table...
712711
sql += try "\(kind.rawValue) \(relation.source.sql(db, &context))"
713712

714713
let rightAlias = relation.sourceAlias
715714
switch target {
716715
case .all:
717-
let filters = try condition.expressions(db, leftAlias: leftAlias, rightAlias: rightAlias)
718-
+ relation.filtersPromise.resolve(db)
716+
// ... ON <join conditions> AND <other filters>
717+
var filters = try condition.expressions(db, leftAlias: leftAlias)
718+
filters += try relation.filtersPromise.resolve(db)
719719
if filters.isEmpty == false {
720-
sql += " ON \(filters.joined(operator: .and).expressionSQL(&context, wrappedInParenthesis: false))"
720+
let filterSQL = filters
721+
.joined(operator: .and)
722+
.qualifiedExpression(with: rightAlias)
723+
.expressionSQL(&context, wrappedInParenthesis: false)
724+
sql += " ON \(filterSQL)"
721725
}
722-
case let .subRelation(subRelation):
726+
case var .first(subRelation):
727+
// Subquery: SELECT id ...
723728
guard let primaryKey = try subRelation.primaryKeyExpression(db) else {
724-
fatalError("Not implemented")
729+
fatalError("Not implemented: support for WITHOUT ROWID optimization")
725730
}
726-
let subAlias = TableAlias()
727-
let filters = try condition.expressions(db, leftAlias: leftAlias, rightAlias: subAlias)
728-
let subRelation = subRelation
729-
.qualified(with: subAlias)
730-
.selectOnly([primaryKey])
731-
.map(\.filtersPromise) { $0.flatMap { DatabasePromise(value: filters + $0) } }
731+
subRelation = subRelation.select([primaryKey])
732+
733+
// Subquery: ... WHERE <condition filters> AND <other filters>
734+
let filters = try condition.expressions(db, leftAlias: leftAlias)
735+
subRelation = subRelation.prependingFilters(filters)
736+
737+
// Subquery: ... LIMIT 1
732738
let subQuery = SQLQuery(relation: subRelation, limit: SQLLimit(limit: 1, offset: nil))
733-
let subQueryGenerator = SQLQueryGenerator(subQuery)
734739

740+
// ... ON id = (SELECT id FROM table WHERE <condition filters> AND <other filters> LIMIT 1)
735741
sql += " ON "
736742
sql += rightAlias[primaryKey].expressionSQL(&context, wrappedInParenthesis: false)
737-
sql += try " = (" + subQueryGenerator.sql(db, &context) + ")"
743+
sql += " = ("
744+
sql += try SQLQueryGenerator(subQuery).sql(db, &context)
745+
sql += ")"
738746
}
739747

740748
for (_, join) in relation.joins {

0 commit comments

Comments
 (0)