Skip to content

Commit dc6fbfe

Browse files
author
Kyle Jessup
committed
Added funcs for extra insert and update features
1 parent 773044b commit dc6fbfe

File tree

3 files changed

+373
-26
lines changed

3 files changed

+373
-26
lines changed

Sources/PerfectPostgreSQL/PostgresCRUDInsert.swift

Lines changed: 128 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,81 @@
88
import Foundation
99
import PerfectCRUD
1010

11+
// Promises that instances.count == returnValue.count on exit
12+
// otherwise it will throw
13+
private func _insert<OverAllForm: Codable, FromTableType: TableProtocol>(fromTable ft: FromTableType,
14+
instances: [OverAllForm],
15+
includeKeys: [PartialKeyPath<OverAllForm>],
16+
excludeKeys: [PartialKeyPath<OverAllForm>]) throws -> [OverAllForm] {
17+
typealias OAF = OverAllForm
18+
let delegate = ft.databaseConfiguration.sqlGenDelegate
19+
var state = SQLGenState(delegate: delegate)
20+
state.command = .insert
21+
try ft.setState(state: &state)
22+
let td = state.tableData[0]
23+
let kpDecoder = td.keyPathDecoder
24+
guard let kpInstance = td.modelInstance else {
25+
throw CRUDSQLGenError("Could not get model instance for key path decoder \(OAF.self)")
26+
}
27+
guard let databaseConfiguration = ft.databaseConfiguration as? PostgresDatabaseConfiguration else {
28+
throw CRUDSQLGenError("This is for Postgres only.")
29+
}
30+
let includeNames: [String]
31+
if includeKeys.isEmpty {
32+
let columnDecoder = CRUDColumnNameDecoder()
33+
_ = try OverAllForm.init(from: columnDecoder)
34+
includeNames = columnDecoder.collectedKeys.map { $0.name }
35+
} else {
36+
includeNames = try includeKeys.map {
37+
guard let n = try kpDecoder.getKeyPathName(kpInstance, keyPath: $0) else {
38+
throw CRUDSQLGenError("Could not get key path name for \(OAF.self) \($0)")
39+
}
40+
return n
41+
}
42+
}
43+
let excludeNames: [String] = try excludeKeys.map {
44+
guard let n = try kpDecoder.getKeyPathName(kpInstance, keyPath: $0) else {
45+
throw CRUDSQLGenError("Could not get key path name for \(OAF.self) \($0)")
46+
}
47+
return n
48+
}
49+
50+
let encoder = try CRUDBindingsEncoder(delegate: delegate)
51+
try instances[0].encode(to: encoder)
52+
53+
let bindings = try encoder.completedBindings(allKeys: includeNames, ignoreKeys: Set(excludeNames))
54+
let columnNames = try bindings.map { try delegate.quote(identifier: $0.column) }
55+
let bindIdentifiers = bindings.map { $0.identifier }
56+
57+
let nameQ = try delegate.quote(identifier: "\(OAF.CRUDTableName)")
58+
let sqlStr = """
59+
INSERT INTO \(nameQ) (\(columnNames.joined(separator: ", ")))
60+
VALUES (\(bindIdentifiers.joined(separator: ", ")))
61+
RETURNING *
62+
"""
63+
CRUDLogging.log(.query, sqlStr)
64+
let exeDelegate = PostgresExeDelegate(connection: databaseConfiguration.connection, sql: sqlStr)
65+
try exeDelegate.bind(delegate.bindings)
66+
guard try exeDelegate.hasNext() else {
67+
throw CRUDSQLGenError("Did not get return value from statement \(sqlStr).")
68+
}
69+
var ret: [OverAllForm] = []
70+
ret.append(try OverAllForm(from: CRUDRowDecoder<ColumnKey>(delegate: exeDelegate)))
71+
for instance in instances[1...] {
72+
exeDelegate.resetResults()
73+
let delegate = databaseConfiguration.sqlGenDelegate
74+
let encoder = try CRUDBindingsEncoder(delegate: delegate)
75+
try instance.encode(to: encoder)
76+
_ = try encoder.completedBindings(allKeys: includeNames, ignoreKeys: Set(excludeNames))
77+
try exeDelegate.bind(delegate.bindings)
78+
guard try exeDelegate.hasNext() else {
79+
throw CRUDSQLGenError("Did not get return value from statement \(sqlStr).")
80+
}
81+
ret.append(try OverAllForm(from: CRUDRowDecoder<ColumnKey>(delegate: exeDelegate)))
82+
}
83+
return ret
84+
}
85+
1186
// Promises that instances.count == returnValue.count on exit
1287
// otherwise it will throw
1388
private func _insert<OverAllForm: Codable, FromTableType: TableProtocol, R: Decodable>(fromTable ft: FromTableType,
@@ -90,27 +165,69 @@ private func _insert<OverAllForm: Codable, FromTableType: TableProtocol, R: Deco
90165
}
91166

92167
public extension Table where C.Configuration == PostgresDatabaseConfiguration {
93-
func insert<R: Decodable>(_ instance: Form, returning: KeyPath<OverAllForm, R>) throws -> R {
168+
/// Insert the instance and return the new column value.
169+
func returning<R: Decodable>(_ returning: KeyPath<OverAllForm, R>, insert instance: Form) throws -> R {
94170
return try _insert(fromTable: self, instances: [instance], returning: returning, includeKeys: [], excludeKeys: []).first!
95171
}
96-
func insert<R: Decodable>(_ instance: Form, returning: KeyPath<OverAllForm, R>,
97-
setKeys: PartialKeyPath<OverAllForm>, _ rest: PartialKeyPath<OverAllForm>...) throws -> R {
172+
/// Insert the instance and return the new column value.
173+
func returning<R: Decodable, Z: Decodable>(_ returning: KeyPath<OverAllForm, R>, insert instance: Form,
174+
setKeys: KeyPath<OverAllForm, Z>, _ rest: PartialKeyPath<OverAllForm>...) throws -> R {
98175
return try _insert(fromTable: self, instances: [instance], returning: returning, includeKeys: [setKeys] + rest, excludeKeys: []).first!
99176
}
100-
func insert<R: Decodable>(_ instance: Form, returning: KeyPath<OverAllForm, R>,
101-
ignoreKeys: PartialKeyPath<OverAllForm>, _ rest: PartialKeyPath<OverAllForm>...) throws -> R {
177+
/// Insert the instance and return the new column value.
178+
func returning<R: Decodable, Z: Decodable>(_ returning: KeyPath<OverAllForm, R>, insert instance: Form,
179+
ignoreKeys: KeyPath<OverAllForm, Z>, _ rest: PartialKeyPath<OverAllForm>...) throws -> R {
102180
return try _insert(fromTable: self, instances: [instance], returning: returning, includeKeys: [], excludeKeys: [ignoreKeys] + rest).first!
103181
}
104-
105-
func insert<R: Decodable>(_ instances: [Form], returning: KeyPath<OverAllForm, R>) throws -> [R] {
182+
/// Insert the instances and return the new column values.
183+
/// Guarantees that insert.count == returnValue.count.
184+
func returning<R: Decodable>(_ returning: KeyPath<OverAllForm, R>, insert instances: [Form]) throws -> [R] {
106185
return try _insert(fromTable: self, instances: instances, returning: returning, includeKeys: [], excludeKeys: [])
107186
}
108-
func insert<R: Decodable>(_ instances: [Form], returning: KeyPath<OverAllForm, R>,
109-
setKeys: PartialKeyPath<OverAllForm>, _ rest: PartialKeyPath<OverAllForm>...) throws -> [R] {
187+
/// Insert the instances and return the new column values.
188+
/// Guarantees that insert.count == returnValue.count.
189+
func returning<R: Decodable, Z: Decodable>(_ returning: KeyPath<OverAllForm, R>, insert instances: [Form],
190+
setKeys: KeyPath<OverAllForm, Z>, _ rest: PartialKeyPath<OverAllForm>...) throws -> [R] {
110191
return try _insert(fromTable: self, instances: instances, returning: returning, includeKeys: [setKeys] + rest, excludeKeys: [])
111192
}
112-
func insert<R: Decodable>(_ instances: [Form], returning: KeyPath<OverAllForm, R>,
113-
ignoreKeys: PartialKeyPath<OverAllForm>, _ rest: PartialKeyPath<OverAllForm>...) throws -> [R] {
193+
/// Insert the instances and return the new column values.
194+
/// Guarantees that insert.count == returnValue.count.
195+
func returning<R: Decodable, Z: Decodable>(_ returning: KeyPath<OverAllForm, R>, insert instances: [Form],
196+
ignoreKeys: KeyPath<OverAllForm, Z>, _ rest: PartialKeyPath<OverAllForm>...) throws -> [R] {
114197
return try _insert(fromTable: self, instances: instances, returning: returning, includeKeys: [], excludeKeys: [ignoreKeys] + rest)
115198
}
116199
}
200+
201+
public extension Table where C.Configuration == PostgresDatabaseConfiguration {
202+
/// Insert the instance and return the new object value.
203+
func returning(insert instance: Form) throws -> OverAllForm {
204+
return try _insert(fromTable: self, instances: [instance], includeKeys: [], excludeKeys: []).first!
205+
}
206+
/// Insert the instance and return the new object value.
207+
func returning<Z: Decodable>(insert instance: Form,
208+
setKeys: KeyPath<OverAllForm, Z>, _ rest: PartialKeyPath<OverAllForm>...) throws -> OverAllForm {
209+
return try _insert(fromTable: self, instances: [instance], includeKeys: [setKeys] + rest, excludeKeys: []).first!
210+
}
211+
/// Insert the instance and return the new object value.
212+
func returning<Z: Decodable>(insert instance: Form,
213+
ignoreKeys: KeyPath<OverAllForm, Z>, _ rest: PartialKeyPath<OverAllForm>...) throws -> OverAllForm {
214+
return try _insert(fromTable: self, instances: [instance], includeKeys: [], excludeKeys: [ignoreKeys] + rest).first!
215+
}
216+
/// Insert the instances and return the new object values.
217+
/// Guarantees that insert.count == returnValue.count.
218+
func returning(insert instances: [Form]) throws -> [OverAllForm] {
219+
return try _insert(fromTable: self, instances: instances, includeKeys: [], excludeKeys: [])
220+
}
221+
/// Insert the instances and return the new object values.
222+
/// Guarantees that insert.count == returnValue.count.
223+
func returning<Z: Decodable>(insert instances: [Form],
224+
setKeys: KeyPath<OverAllForm, Z>, _ rest: PartialKeyPath<OverAllForm>...) throws -> [OverAllForm] {
225+
return try _insert(fromTable: self, instances: instances, includeKeys: [setKeys] + rest, excludeKeys: [])
226+
}
227+
/// Insert the instances and return the new object values.
228+
/// Guarantees that insert.count == returnValue.count.
229+
func returning<Z: Decodable>(insert instances: [Form],
230+
ignoreKeys: KeyPath<OverAllForm, Z>, _ rest: PartialKeyPath<OverAllForm>...) throws -> [OverAllForm] {
231+
return try _insert(fromTable: self, instances: instances, includeKeys: [], excludeKeys: [ignoreKeys] + rest)
232+
}
233+
}
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
//
2+
// PostgresCRUDUpdate.swift
3+
// PerfectPostgreSQL
4+
//
5+
// Created by Kyle Jessup on 2019-01-30.
6+
//
7+
8+
import Foundation
9+
import PerfectCRUD
10+
11+
// Promises that instances.count == returnValue.count on exit
12+
// otherwise it will throw
13+
private func _update<OverAllForm: Codable, FromTableType: TableProtocol>(fromTable ft: FromTableType,
14+
instance: OverAllForm,
15+
includeKeys: [PartialKeyPath<OverAllForm>],
16+
excludeKeys: [PartialKeyPath<OverAllForm>]) throws -> [OverAllForm] {
17+
typealias OAF = OverAllForm
18+
let delegate = ft.databaseConfiguration.sqlGenDelegate
19+
var state = SQLGenState(delegate: delegate)
20+
state.command = .update
21+
try ft.setState(state: &state)
22+
let td = state.tableData[0]
23+
let kpDecoder = td.keyPathDecoder
24+
guard let kpInstance = td.modelInstance else {
25+
throw CRUDSQLGenError("Could not get model instance for key path decoder \(OAF.self)")
26+
}
27+
guard let databaseConfiguration = ft.databaseConfiguration as? PostgresDatabaseConfiguration else {
28+
throw CRUDSQLGenError("This is for Postgres only.")
29+
}
30+
let includeNames: [String]
31+
if includeKeys.isEmpty {
32+
let columnDecoder = CRUDColumnNameDecoder()
33+
_ = try OverAllForm.init(from: columnDecoder)
34+
includeNames = columnDecoder.collectedKeys.map { $0.name }
35+
} else {
36+
includeNames = try includeKeys.map {
37+
guard let n = try kpDecoder.getKeyPathName(kpInstance, keyPath: $0) else {
38+
throw CRUDSQLGenError("Could not get key path name for \(OAF.self) \($0)")
39+
}
40+
return n
41+
}
42+
}
43+
let excludeNames: [String] = try excludeKeys.map {
44+
guard let n = try kpDecoder.getKeyPathName(kpInstance, keyPath: $0) else {
45+
throw CRUDSQLGenError("Could not get key path name for \(OAF.self) \($0)")
46+
}
47+
return n
48+
}
49+
50+
let encoder = try CRUDBindingsEncoder(delegate: delegate)
51+
try instance.encode(to: encoder)
52+
state.bindingsEncoder = encoder
53+
state.columnFilters = (include: includeNames, exclude: excludeNames)
54+
try ft.setSQL(state: &state)
55+
var ret: [OverAllForm] = []
56+
if let stat = state.statements.first { // multi statements?!
57+
let sql = stat.sql + " RETURNING *"
58+
let exeDelegate = try databaseConfiguration.sqlExeDelegate(forSQL: sql)
59+
try exeDelegate.bind(stat.bindings)
60+
while try exeDelegate.hasNext() {
61+
ret.append(try OverAllForm(from: CRUDRowDecoder<ColumnKey>(delegate: exeDelegate)))
62+
}
63+
}
64+
return ret
65+
}
66+
67+
// Promises that instances.count == returnValue.count on exit
68+
// otherwise it will throw
69+
private func _update<OverAllForm: Codable, FromTableType: TableProtocol, R: Decodable>(fromTable ft: FromTableType,
70+
instance: OverAllForm,
71+
returning: KeyPath<OverAllForm, R>,
72+
includeKeys: [PartialKeyPath<OverAllForm>],
73+
excludeKeys: [PartialKeyPath<OverAllForm>]) throws -> [R] {
74+
typealias OAF = OverAllForm
75+
let delegate = ft.databaseConfiguration.sqlGenDelegate
76+
var state = SQLGenState(delegate: delegate)
77+
state.command = .update
78+
try ft.setState(state: &state)
79+
let td = state.tableData[0]
80+
let kpDecoder = td.keyPathDecoder
81+
guard let kpInstance = td.modelInstance else {
82+
throw CRUDSQLGenError("Could not get model instance for key path decoder \(OAF.self)")
83+
}
84+
guard let returningName = try kpDecoder.getKeyPathName(kpInstance, keyPath: returning) else {
85+
throw CRUDSQLGenError("Could not get column name for `returning` key path \(returning).")
86+
}
87+
guard let databaseConfiguration = ft.databaseConfiguration as? PostgresDatabaseConfiguration else {
88+
throw CRUDSQLGenError("This is for Postgres only.")
89+
}
90+
let includeNames: [String]
91+
if includeKeys.isEmpty {
92+
let columnDecoder = CRUDColumnNameDecoder()
93+
_ = try OverAllForm.init(from: columnDecoder)
94+
includeNames = columnDecoder.collectedKeys.map { $0.name }
95+
} else {
96+
includeNames = try includeKeys.map {
97+
guard let n = try kpDecoder.getKeyPathName(kpInstance, keyPath: $0) else {
98+
throw CRUDSQLGenError("Could not get key path name for \(OAF.self) \($0)")
99+
}
100+
return n
101+
}
102+
}
103+
let excludeNames: [String] = try excludeKeys.map {
104+
guard let n = try kpDecoder.getKeyPathName(kpInstance, keyPath: $0) else {
105+
throw CRUDSQLGenError("Could not get key path name for \(OAF.self) \($0)")
106+
}
107+
return n
108+
}
109+
110+
let encoder = try CRUDBindingsEncoder(delegate: delegate)
111+
try instance.encode(to: encoder)
112+
state.bindingsEncoder = encoder
113+
state.columnFilters = (include: includeNames, exclude: excludeNames)
114+
try ft.setSQL(state: &state)
115+
var ret: [R] = []
116+
if let stat = state.statements.first { // multi statements?!
117+
let nameQ = try delegate.quote(identifier: "\(OAF.CRUDTableName)")
118+
let sql = stat.sql + " RETURNING \(nameQ).\(try delegate.quote(identifier: returningName))"
119+
let exeDelegate = try databaseConfiguration.sqlExeDelegate(forSQL: sql)
120+
try exeDelegate.bind(stat.bindings)
121+
while try exeDelegate.hasNext(), let next: KeyedDecodingContainer<ColumnKey> = try exeDelegate.next() {
122+
let value = try next.decode(R.self, forKey: ColumnKey(stringValue: returningName)!)
123+
ret.append(value)
124+
}
125+
}
126+
return ret
127+
}
128+
129+
public extension Table where C.Configuration == PostgresDatabaseConfiguration {
130+
/// Update the instance and return the new column value.
131+
func returning<R: Decodable>(_ returning: KeyPath<OverAllForm, R>, update instance: Form) throws -> [R] {
132+
return try _update(fromTable: self, instance: instance, returning: returning, includeKeys: [], excludeKeys: [])
133+
}
134+
/// Update the instance and return the new column value.
135+
func returning<R: Decodable, Z: Decodable>(_ returning: KeyPath<OverAllForm, R>, update instance: Form,
136+
setKeys: KeyPath<OverAllForm, Z>, _ rest: PartialKeyPath<OverAllForm>...) throws -> [R] {
137+
return try _update(fromTable: self, instance: instance, returning: returning, includeKeys: [setKeys] + rest, excludeKeys: [])
138+
}
139+
/// Update the instance and return the new column value.
140+
func returning<R: Decodable, Z: Decodable>(_ returning: KeyPath<OverAllForm, R>, update instance: Form,
141+
ignoreKeys: KeyPath<OverAllForm, Z>, _ rest: PartialKeyPath<OverAllForm>...) throws -> [R] {
142+
return try _update(fromTable: self, instance: instance, returning: returning, includeKeys: [], excludeKeys: [ignoreKeys] + rest)
143+
}
144+
/// Update the instance and return the new object value.
145+
func returning(update instance: Form) throws -> [OverAllForm] {
146+
return try _update(fromTable: self, instance: instance, includeKeys: [], excludeKeys: [])
147+
}
148+
/// Update the instance and return the new object value.
149+
func returning<Z: Decodable>(update instance: Form,
150+
setKeys: KeyPath<OverAllForm, Z>, _ rest: PartialKeyPath<OverAllForm>...) throws -> [OverAllForm] {
151+
return try _update(fromTable: self, instance: instance, includeKeys: [setKeys] + rest, excludeKeys: [])
152+
}
153+
/// Update the instance and return the new object value.
154+
func returning<Z: Decodable>(update instance: Form,
155+
ignoreKeys: KeyPath<OverAllForm, Z>, _ rest: PartialKeyPath<OverAllForm>...) throws -> [OverAllForm] {
156+
return try _update(fromTable: self, instance: instance, includeKeys: [], excludeKeys: [ignoreKeys] + rest)
157+
}
158+
}
159+
160+
public extension Where where OverAllForm == FromTableType.Form {
161+
/// Update the instance and return the new column value.
162+
func returning<R: Decodable>(_ returning: KeyPath<OverAllForm, R>, update instance: Form) throws -> [R] {
163+
return try _update(fromTable: self, instance: instance, returning: returning, includeKeys: [], excludeKeys: [])
164+
}
165+
/// Update the instance and return the new column value.
166+
func returning<R: Decodable, Z: Decodable>(_ returning: KeyPath<OverAllForm, R>, update instance: Form,
167+
setKeys: KeyPath<OverAllForm, Z>, _ rest: PartialKeyPath<OverAllForm>...) throws -> [R] {
168+
return try _update(fromTable: self, instance: instance, returning: returning, includeKeys: [setKeys] + rest, excludeKeys: [])
169+
}
170+
/// Update the instance and return the new column value.
171+
func returning<R: Decodable, Z: Decodable>(_ returning: KeyPath<OverAllForm, R>, update instance: Form,
172+
ignoreKeys: KeyPath<OverAllForm, Z>, _ rest: PartialKeyPath<OverAllForm>...) throws -> [R] {
173+
return try _update(fromTable: self, instance: instance, returning: returning, includeKeys: [], excludeKeys: [ignoreKeys] + rest)
174+
}
175+
/// Update the instance and return the new object value.
176+
func returning(update instance: Form) throws -> [OverAllForm] {
177+
return try _update(fromTable: self, instance: instance, includeKeys: [], excludeKeys: [])
178+
}
179+
/// Update the instance and return the new object value.
180+
func returning<Z: Decodable>(update instance: Form,
181+
setKeys: KeyPath<OverAllForm, Z>, _ rest: PartialKeyPath<OverAllForm>...) throws -> [OverAllForm] {
182+
return try _update(fromTable: self, instance: instance, includeKeys: [setKeys] + rest, excludeKeys: [])
183+
}
184+
/// Update the instance and return the new object value.
185+
func returning<Z: Decodable>(update instance: Form,
186+
ignoreKeys: KeyPath<OverAllForm, Z>, _ rest: PartialKeyPath<OverAllForm>...) throws -> [OverAllForm] {
187+
return try _update(fromTable: self, instance: instance, includeKeys: [], excludeKeys: [ignoreKeys] + rest)
188+
}
189+
}

0 commit comments

Comments
 (0)