diff --git a/Sources/StructuredQueriesCore/Internal/Deprecations.swift b/Sources/StructuredQueriesCore/Internal/Deprecations.swift index b9920caf..69572841 100644 --- a/Sources/StructuredQueriesCore/Internal/Deprecations.swift +++ b/Sources/StructuredQueriesCore/Internal/Deprecations.swift @@ -45,7 +45,7 @@ extension Table { public static func insert( or conflictResolution: ConflictResolution? = nil, _ row: Self, - onConflict doUpdate: ((inout Updates) -> Void)? = nil + onConflict doUpdate: ((inout Upsert) -> Void)? = nil ) -> InsertOf { insert(or: conflictResolution, [row], onConflict: doUpdate) } @@ -56,7 +56,7 @@ extension Table { public static func insert( or conflictResolution: ConflictResolution? = nil, _ rows: [Self], - onConflict doUpdate: ((inout Updates) -> Void)? = nil + onConflict doUpdate: ((inout Upsert) -> Void)? = nil ) -> InsertOf { insert(or: conflictResolution, values: { rows }, onConflict: doUpdate) } @@ -66,7 +66,7 @@ extension Table { or conflictResolution: ConflictResolution? = nil, _ columns: (TableColumns) -> TableColumns = { $0 }, @InsertValuesBuilder values: () -> [Self], - onConflict updates: ((inout Updates) -> Void)? + onConflict updates: ((inout Upsert) -> Void)? ) -> InsertOf { insert(or: conflictResolution, columns, values: values, onConflictDoUpdate: updates) } @@ -77,7 +77,7 @@ extension Table { _ columns: (TableColumns) -> (TableColumn, repeat TableColumn), @InsertValuesBuilder<(V1.QueryOutput, repeat (each V2).QueryOutput)> values: () -> [(V1.QueryOutput, repeat (each V2).QueryOutput)], - onConflict updates: ((inout Updates) -> Void)? + onConflict updates: ((inout Upsert) -> Void)? ) -> InsertOf { insert(or: conflictResolution, columns, values: values, onConflictDoUpdate: updates) } @@ -89,7 +89,7 @@ extension Table { or conflictResolution: ConflictResolution? = nil, _ columns: (TableColumns) -> (TableColumn, repeat TableColumn), select selection: () -> Select<(V1, repeat each V2), From, Joins>, - onConflict updates: ((inout Updates) -> Void)? + onConflict updates: ((inout Upsert) -> Void)? ) -> InsertOf { insert(or: conflictResolution, columns, select: selection, onConflictDoUpdate: updates) } @@ -102,7 +102,7 @@ extension PrimaryKeyedTable { public static func insert( or conflictResolution: ConflictResolution? = nil, _ row: Draft, - onConflict updates: ((inout Updates) -> Void)? = nil + onConflict updates: ((inout Upsert) -> Void)? = nil ) -> InsertOf { insert(or: conflictResolution, values: { row }, onConflictDoUpdate: updates) } @@ -113,7 +113,7 @@ extension PrimaryKeyedTable { public static func insert( or conflictResolution: ConflictResolution? = nil, _ rows: [Draft], - onConflict updates: ((inout Updates) -> Void)? = nil + onConflict updates: ((inout Upsert) -> Void)? = nil ) -> InsertOf { insert(or: conflictResolution, values: { rows }, onConflictDoUpdate: updates) } diff --git a/Sources/StructuredQueriesCore/Statements/Insert.swift b/Sources/StructuredQueriesCore/Statements/Insert.swift index a48bef49..8de2a539 100644 --- a/Sources/StructuredQueriesCore/Statements/Insert.swift +++ b/Sources/StructuredQueriesCore/Statements/Insert.swift @@ -54,7 +54,7 @@ extension Table { or conflictResolution: ConflictResolution? = nil, _ columns: (TableColumns) -> TableColumns = { $0 }, @InsertValuesBuilder values: () -> [Self], - onConflictDoUpdate updates: ((inout Updates) -> Void)? = nil, + onConflictDoUpdate updates: ((inout Upsert) -> Void)? = nil, @QueryFragmentBuilder where updateFilter: (TableColumns) -> [QueryFragment] = { _ in [] } ) -> InsertOf { @@ -89,7 +89,7 @@ extension Table { ), @QueryFragmentBuilder where targetFilter: (TableColumns) -> [QueryFragment] = { _ in [] }, - doUpdate updates: (inout Updates) -> Void = { _ in }, + doUpdate updates: (inout Upsert) -> Void = { _ in }, @QueryFragmentBuilder where updateFilter: (TableColumns) -> [QueryFragment] = { _ in [] } ) -> InsertOf { @@ -111,7 +111,7 @@ extension Table { onConflict conflictTargets: (TableColumns) -> (repeat TableColumn)?, @QueryFragmentBuilder where targetFilter: (TableColumns) -> [QueryFragment] = { _ in [] }, - doUpdate updates: ((inout Updates) -> Void)?, + doUpdate updates: ((inout Upsert) -> Void)?, @QueryFragmentBuilder where updateFilter: (TableColumns) -> [QueryFragment] = { _ in [] } ) -> InsertOf { @@ -197,7 +197,7 @@ extension Table { _ columns: (TableColumns) -> (TableColumn, repeat TableColumn), @InsertValuesBuilder<(V1.QueryOutput, repeat (each V2).QueryOutput)> values: () -> [(V1.QueryOutput, repeat (each V2).QueryOutput)], - onConflictDoUpdate updates: ((inout Updates) -> Void)? = nil, + onConflictDoUpdate updates: ((inout Upsert) -> Void)? = nil, @QueryFragmentBuilder where updateFilter: (TableColumns) -> [QueryFragment] = { _ in [] } ) -> InsertOf { @@ -234,7 +234,7 @@ extension Table { ), @QueryFragmentBuilder where targetFilter: (TableColumns) -> [QueryFragment] = { _ in [] }, - doUpdate updates: (inout Updates) -> Void = { _ in }, + doUpdate updates: (inout Upsert) -> Void = { _ in }, @QueryFragmentBuilder where updateFilter: (TableColumns) -> [QueryFragment] = { _ in [] } ) -> InsertOf { @@ -259,7 +259,7 @@ extension Table { onConflict conflictTargets: (TableColumns) -> (repeat TableColumn)?, @QueryFragmentBuilder where targetFilter: (TableColumns) -> [QueryFragment] = { _ in [] }, - doUpdate updates: ((inout Updates) -> Void)?, + doUpdate updates: ((inout Upsert) -> Void)?, @QueryFragmentBuilder where updateFilter: (TableColumns) -> [QueryFragment] = { _ in [] } ) -> InsertOf { @@ -306,7 +306,7 @@ extension Table { or conflictResolution: ConflictResolution? = nil, _ columns: (TableColumns) -> (TableColumn, repeat TableColumn), select selection: () -> some PartialSelectStatement<(V1, repeat each V2)>, - onConflictDoUpdate updates: ((inout Updates) -> Void)? = nil, + onConflictDoUpdate updates: ((inout Upsert) -> Void)? = nil, @QueryFragmentBuilder where updateFilter: (TableColumns) -> [QueryFragment] = { _ in [] } ) -> InsertOf { @@ -350,7 +350,7 @@ extension Table { ), @QueryFragmentBuilder where targetFilter: (TableColumns) -> [QueryFragment] = { _ in [] }, - doUpdate updates: (inout Updates) -> Void = { _ in }, + doUpdate updates: (inout Upsert) -> Void = { _ in }, @QueryFragmentBuilder where updateFilter: (TableColumns) -> [QueryFragment] = { _ in [] } ) -> InsertOf { @@ -377,7 +377,7 @@ extension Table { onConflict conflictTargets: (TableColumns) -> (repeat TableColumn)?, @QueryFragmentBuilder where targetFilter: (TableColumns) -> [QueryFragment] = { _ in [] }, - doUpdate updates: ((inout Updates) -> Void)?, + doUpdate updates: ((inout Upsert) -> Void)?, @QueryFragmentBuilder where updateFilter: (TableColumns) -> [QueryFragment] = { _ in [] } ) -> InsertOf { @@ -428,7 +428,7 @@ extension Table { onConflict conflictTargets: (TableColumns) -> (repeat TableColumn)?, @QueryFragmentBuilder where targetFilter: (TableColumns) -> [QueryFragment] = { _ in [] }, - doUpdate updates: ((inout Updates) -> Void)?, + doUpdate updates: ((inout Upsert) -> Void)?, @QueryFragmentBuilder where updateFilter: (TableColumns) -> [QueryFragment] = { _ in [] } ) -> InsertOf { @@ -444,7 +444,7 @@ extension Table { conflictTargetColumnNames: conflictTargetColumnNames, conflictTargetFilter: targetFilter(Self.columns), values: values, - updates: updates.map { Updates($0) }, + updates: updates.map { Upsert($0) }, updateFilter: updateFilter(Self.columns), returning: [] ) @@ -468,7 +468,7 @@ extension PrimaryKeyedTable { or conflictResolution: ConflictResolution? = nil, _ columns: (Draft.TableColumns) -> Draft.TableColumns = { $0 }, @InsertValuesBuilder values: () -> [Draft], - onConflictDoUpdate updates: ((inout Updates) -> Void)? = nil, + onConflictDoUpdate updates: ((inout Upsert) -> Void)? = nil, @QueryFragmentBuilder where updateFilter: (TableColumns) -> [QueryFragment] = { _ in [] } ) -> InsertOf { @@ -504,7 +504,7 @@ extension PrimaryKeyedTable { ), @QueryFragmentBuilder where targetFilter: (TableColumns) -> [QueryFragment] = { _ in [] }, - doUpdate updates: (inout Updates) -> Void = { _ in }, + doUpdate updates: (inout Upsert) -> Void = { _ in }, @QueryFragmentBuilder where updateFilter: (TableColumns) -> [QueryFragment] = { _ in [] } ) -> InsertOf { @@ -545,7 +545,7 @@ extension PrimaryKeyedTable { onConflict: { $0.primaryKey }, doUpdate: { updates in for column in Draft.TableColumns.allColumns where column.name != columns.primaryKey.name { - updates.set(column, #""excluded".\#(quote: column.name)"#) + updates.updates.set(column, #""excluded".\#(quote: column.name)"#) } } ) @@ -557,7 +557,7 @@ extension PrimaryKeyedTable { onConflict conflictTargets: (TableColumns) -> (repeat TableColumn)?, @QueryFragmentBuilder where targetFilter: (TableColumns) -> [QueryFragment] = { _ in [] }, - doUpdate updates: ((inout Updates) -> Void)?, + doUpdate updates: ((inout Upsert) -> Void)?, @QueryFragmentBuilder where updateFilter: (TableColumns) -> [QueryFragment] = { _ in [] } ) -> InsertOf { @@ -590,6 +590,49 @@ private enum InsertValues { case select(QueryFragment) } +@dynamicMemberLookup +public struct Upsert: QueryExpression { + public typealias QueryValue = Never + + public struct Excluded: AliasName { + public static var aliasName: String { + "excluded" + } + } + + init(_ body: (inout Self) -> Void) { + body(&self) + } + + fileprivate var updates = Updates() + + public var excluded: TableAlias.TableColumns { + Base.as(Excluded.self).columns + } + + public subscript(dynamicMember keyPath: KeyPath, Member>) -> Member { + updates[keyPath: keyPath] + } + + public subscript( + dynamicMember keyPath: WritableKeyPath, Member> + ) -> Member { + get { updates[keyPath: keyPath] } + set { updates[keyPath: keyPath] = newValue } + } + + public subscript( + dynamicMember keyPath: KeyPath> + ) -> any QueryExpression { + get { updates[dynamicMember: keyPath] } + set { updates[dynamicMember: keyPath] = newValue } + } + + public var queryFragment: QueryFragment { + updates.queryFragment + } +} + /// An `INSERT` statement. /// /// This type of statement is returned from the @@ -603,7 +646,7 @@ public struct Insert { var conflictTargetColumnNames: [String] var conflictTargetFilter: [QueryFragment] fileprivate var values: InsertValues - var updates: Updates? + var updates: Upsert? var updateFilter: [QueryFragment] var returning: [QueryFragment] @@ -613,7 +656,7 @@ public struct Insert { conflictTargetColumnNames: [String], conflictTargetFilter: [QueryFragment], values: InsertValues, - updates: Updates?, + updates: Upsert?, updateFilter: [QueryFragment], returning: [QueryFragment] ) { diff --git a/Sources/StructuredQueriesCore/Updates.swift b/Sources/StructuredQueriesCore/Updates.swift index 1c14b35e..dab601cd 100644 --- a/Sources/StructuredQueriesCore/Updates.swift +++ b/Sources/StructuredQueriesCore/Updates.swift @@ -8,6 +8,9 @@ public struct Updates { private var updates: [(String, QueryFragment)] = [] + init() { + } + init(_ body: (inout Self) -> Void) { body(&self) } diff --git a/Tests/StructuredQueriesTests/InsertTests.swift b/Tests/StructuredQueriesTests/InsertTests.swift index c7268823..9ede2f21 100644 --- a/Tests/StructuredQueriesTests/InsertTests.swift +++ b/Tests/StructuredQueriesTests/InsertTests.swift @@ -579,7 +579,7 @@ extension SnapshotTests { } where: { !$0.isCompleted } doUpdate: { - $0.isCompleted = true + $0.isCompleted = $0.excluded.isCompleted } where: { $0.isFlagged } @@ -591,7 +591,7 @@ extension SnapshotTests { (NULL, NULL, NULL, 0, 0, '', NULL, 1, '') ON CONFLICT ("id") WHERE NOT ("reminders"."isCompleted") - DO UPDATE SET "isCompleted" = 1 + DO UPDATE SET "isCompleted" = "excluded"."isCompleted" WHERE "reminders"."isFlagged" """ }