Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@

### Transforming queries

- ``unscoped``
- ``all``
- ``none``
- ``map(_:)``
- ``subscript(dynamicMember:)``
- ``StructuredQueriesCore/+(_:_:)``
Expand Down
5 changes: 5 additions & 0 deletions Sources/StructuredQueriesCore/Internal/Scope.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
enum Scope {
case unscoped
case `default`
case empty
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,28 @@ public struct With<QueryValue>: Statement {
}

public var query: QueryFragment {
guard !statement.isEmpty else { return "" }
let cteFragments = ctes.compactMap(\.queryFragment.presence)
guard !cteFragments.isEmpty else { return "" }
var query: QueryFragment = "WITH "
query.append(
"\(ctes.map(\.queryFragment).joined(separator: ", "))\(.newlineOrSpace)\(statement)"
"\(cteFragments.joined(separator: ", "))\(.newlineOrSpace)\(statement)"
)
return query
}
}

extension QueryFragment {
fileprivate var presence: Self? { isEmpty ? nil : self }
}

public struct CommonTableExpressionClause: QueryExpression {
public typealias QueryValue = ()
let tableName: QueryFragment
let select: QueryFragment
public var queryFragment: QueryFragment {
"\(tableName) AS (\(.newline)\(select.indented())\(.newline))"
guard !select.isEmpty else { return "" }
return "\(tableName) AS (\(.newline)\(select.indented())\(.newline))"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ private struct CompoundSelect<QueryValue>: PartialSelectStatement {
}

var query: QueryFragment {
"\(lhs)\(.newlineOrSpace)\(`operator`.indented())\(.newlineOrSpace)\(rhs)"
guard !lhs.isEmpty else { return rhs }
guard !rhs.isEmpty else { return lhs }
return "\(lhs)\(.newlineOrSpace)\(`operator`.indented())\(.newlineOrSpace)\(rhs)"
}
}
6 changes: 5 additions & 1 deletion Sources/StructuredQueriesCore/Statements/Delete.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ extension PrimaryKeyedTable {
/// - Parameter row: A row to delete.
/// - Returns: A delete statement.
public static func delete(_ row: Self) -> DeleteOf<Self> {
Delete()
delete()
.where {
$0.primaryKey.eq(TableColumns.PrimaryKey(queryOutput: row[keyPath: $0.primaryKey.keyPath]))
}
Expand All @@ -36,6 +36,7 @@ extension PrimaryKeyedTable {
///
/// To learn more, see <doc:DeleteStatements>.
public struct Delete<From: Table, Returning> {
var isEmpty: Bool
var `where`: [QueryFragment] = []
var returning: [QueryFragment] = []

Expand Down Expand Up @@ -107,6 +108,7 @@ public struct Delete<From: Table, Returning> {
returning.append("\(quote: resultColumn.name)")
}
return Delete<From, (repeat each QueryValue)>(
isEmpty: isEmpty,
where: `where`,
returning: Array(repeat each selection(From.columns))
)
Expand All @@ -126,6 +128,7 @@ public struct Delete<From: Table, Returning> {
returning.append("\(quote: resultColumn.name)")
}
return Delete<From, From>(
isEmpty: isEmpty,
where: `where`,
returning: returning
)
Expand All @@ -139,6 +142,7 @@ extension Delete: Statement {
public typealias QueryValue = Returning

public var query: QueryFragment {
guard !isEmpty else { return "" }
var query: QueryFragment = "DELETE FROM "
if let schemaName = From.schemaName {
query.append("\(quote: schemaName).")
Expand Down
44 changes: 43 additions & 1 deletion Sources/StructuredQueriesCore/Statements/Select.swift
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ extension Table {
}

public struct _SelectClauses: Sendable {
var isEmpty = false
var distinct = false
var columns: [any QueryExpression] = []
var joins: [_JoinClause] = []
Expand All @@ -320,6 +321,11 @@ public struct Select<Columns, From: Table, Joins> {
// NB: A parameter pack compiler crash forces us to heap-allocate this storage.
@CopyOnWrite var clauses = _SelectClauses()

fileprivate var isEmpty: Bool {
get { clauses.isEmpty }
set { clauses.isEmpty = newValue }
_modify { yield &clauses.isEmpty }
}
fileprivate var distinct: Bool {
get { clauses.distinct }
set { clauses.distinct = newValue }
Expand Down Expand Up @@ -362,6 +368,7 @@ public struct Select<Columns, From: Table, Joins> {
}

fileprivate init(
isEmpty: Bool,
distinct: Bool,
columns: [any QueryExpression],
joins: [_JoinClause],
Expand All @@ -371,6 +378,7 @@ public struct Select<Columns, From: Table, Joins> {
order: [QueryFragment],
limit: _LimitClause?
) {
self.isEmpty = isEmpty
self.columns = columns
self.distinct = distinct
self.joins = joins
Expand All @@ -387,7 +395,8 @@ public struct Select<Columns, From: Table, Joins> {
}

extension Select {
init(where: [QueryFragment] = []) {
init(isEmpty: Bool = false, where: [QueryFragment] = []) {
self.isEmpty = isEmpty
self.where = `where`
}

Expand Down Expand Up @@ -558,6 +567,7 @@ extension Select {
Joins == (repeat each J)
{
Select<(repeat each C1, repeat (each C2).QueryValue), From, (repeat each J)>(
isEmpty: isEmpty,
distinct: distinct,
columns: columns + Array(repeat each selection((From.columns, repeat (each J).columns))),
joins: joins,
Expand Down Expand Up @@ -612,6 +622,7 @@ extension Select {
)
)
return Select<(repeat each C1, repeat each C2), From, (repeat each J1, F, repeat each J2)>(
isEmpty: isEmpty || other.isEmpty,
distinct: distinct || other.distinct,
columns: columns + other.columns,
joins: joins + [join] + other.joins,
Expand Down Expand Up @@ -651,6 +662,7 @@ extension Select {
)
)
return Select<(repeat each C1, repeat each C2), From, (repeat each J, F)>(
isEmpty: isEmpty || other.isEmpty,
distinct: distinct || other.distinct,
columns: columns + other.columns,
joins: joins + [join] + other.joins,
Expand Down Expand Up @@ -686,6 +698,7 @@ extension Select {
)
)
return Select<QueryValue, From, (F, repeat each J)>(
isEmpty: isEmpty || other.isEmpty,
distinct: distinct || other.distinct,
columns: columns + other.columns,
joins: joins + [join] + other.joins,
Expand Down Expand Up @@ -738,6 +751,7 @@ extension Select {
From,
(repeat each J1, F._Optionalized, repeat (each J2)._Optionalized)
>(
isEmpty: isEmpty || other.isEmpty,
distinct: distinct || other.distinct,
columns: columns + other.columns,
joins: joins + [join] + other.joins,
Expand Down Expand Up @@ -785,6 +799,7 @@ extension Select {
From,
(repeat each J, F._Optionalized)
>(
isEmpty: isEmpty || other.isEmpty,
distinct: distinct || other.distinct,
columns: columns + other.columns,
joins: joins + [join] + other.joins,
Expand Down Expand Up @@ -822,6 +837,7 @@ extension Select {
)
)
return Select<QueryValue, From, (F._Optionalized, repeat (each J)._Optionalized)>(
isEmpty: isEmpty || other.isEmpty,
distinct: distinct || other.distinct,
columns: columns + other.columns,
joins: joins + [join] + other.joins,
Expand Down Expand Up @@ -874,6 +890,7 @@ extension Select {
From._Optionalized,
(repeat (each J1)._Optionalized, F, repeat each J2)
>(
isEmpty: isEmpty || other.isEmpty,
distinct: distinct || other.distinct,
columns: columns + other.columns,
joins: joins + [join] + other.joins,
Expand Down Expand Up @@ -921,6 +938,7 @@ extension Select {
From._Optionalized,
(repeat (each J)._Optionalized, F)
>(
isEmpty: isEmpty || other.isEmpty,
distinct: distinct || other.distinct,
columns: columns + other.columns,
joins: joins + [join] + other.joins,
Expand Down Expand Up @@ -958,6 +976,7 @@ extension Select {
)
)
return Select<QueryValue, From._Optionalized, (F, repeat each J)>(
isEmpty: isEmpty || other.isEmpty,
distinct: distinct || other.distinct,
columns: columns + other.columns,
joins: joins + [join] + other.joins,
Expand Down Expand Up @@ -1010,6 +1029,7 @@ extension Select {
From._Optionalized,
(repeat (each J1)._Optionalized, F._Optionalized, repeat (each J2)._Optionalized)
>(
isEmpty: isEmpty || other.isEmpty,
distinct: distinct || other.distinct,
columns: columns + other.columns,
joins: joins + [join] + other.joins,
Expand Down Expand Up @@ -1057,6 +1077,7 @@ extension Select {
From._Optionalized,
(repeat (each J)._Optionalized, F._Optionalized)
>(
isEmpty: isEmpty || other.isEmpty,
distinct: distinct || other.distinct,
columns: columns + other.columns,
joins: joins + [join] + other.joins,
Expand Down Expand Up @@ -1094,6 +1115,7 @@ extension Select {
)
)
return Select<QueryValue, From._Optionalized, (F._Optionalized, repeat (each J)._Optionalized)>(
isEmpty: isEmpty || other.isEmpty,
distinct: distinct || other.distinct,
columns: columns + other.columns,
joins: joins + [join] + other.joins,
Expand Down Expand Up @@ -1348,6 +1370,7 @@ extension Select {
SQLQueryExpression(iterator.next()!.queryFragment)
}
return Select<(repeat (each C2).QueryValue), From, Joins>(
isEmpty: isEmpty,
distinct: distinct,
columns: Array(repeat each transform(repeat { _ in next() }((each C1).self))),
joins: joins,
Expand All @@ -1358,6 +1381,23 @@ extension Select {
limit: limit
)
}

/// Returns a fully unscoped version of this select statement.
public var unscoped: Where<From> {
From.unscoped
}

/// Returns this select statement unchanged.
public var all: Self {
self
}

/// Returns an empty select statement.
public var none: Self {
var select = self
select.isEmpty = true
return select
}
}

/// Combines two select statements of the same table type together.
Expand Down Expand Up @@ -1387,6 +1427,7 @@ public func + <
return Select<
(repeat each C1, repeat each C2), From, (repeat each J1, repeat each J2)
>(
isEmpty: lhs.isEmpty || rhs.isEmpty,
distinct: lhs.distinct || rhs.distinct,
columns: lhs.columns + rhs.columns,
joins: lhs.joins + rhs.joins,
Expand All @@ -1406,6 +1447,7 @@ extension Select: SelectStatement {
}

public var query: QueryFragment {
guard !isEmpty else { return "" }
var query: QueryFragment = "SELECT"
let columns =
columns.isEmpty
Expand Down
5 changes: 4 additions & 1 deletion Sources/StructuredQueriesCore/Statements/Update.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ extension PrimaryKeyedTable {
///
/// To learn more, see <doc:UpdateStatements>.
public struct Update<From: Table, Returning> {
var isEmpty: Bool
var conflictResolution: ConflictResolution?
var updates: Updates<From>
var `where`: [QueryFragment] = []
Expand Down Expand Up @@ -159,6 +160,7 @@ public struct Update<From: Table, Returning> {
returning.append("\(quote: resultColumn.name)")
}
return Update<From, (repeat each QueryValue)>(
isEmpty: false,
conflictResolution: conflictResolution,
updates: updates,
where: `where`,
Expand All @@ -180,6 +182,7 @@ public struct Update<From: Table, Returning> {
returning.append("\(quote: resultColumn.name)")
}
return Update<From, From>(
isEmpty: isEmpty,
conflictResolution: conflictResolution,
updates: updates,
where: `where`,
Expand All @@ -195,7 +198,7 @@ extension Update: Statement {
public typealias QueryValue = Returning

public var query: QueryFragment {
guard !updates.isEmpty
guard !isEmpty, !updates.isEmpty
else { return "" }

var query: QueryFragment = "UPDATE "
Expand Down
Loading