forked from vapor/postgres-kit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPostgreSQLConnection+Query.swift
90 lines (87 loc) · 4.11 KB
/
PostgreSQLConnection+Query.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
extension PostgreSQLConnection {
/// Runs a query, returning each row to the supplied handler.
///
/// try conn.query(.select(.all, from: "users")) { row in
/// print(row)
/// }
///
/// Any values bound to the `DataQuery` as placeholders will be sent as query parameters.
///
/// - parameters:
/// - query: `Query` to execute.
/// - onRow: PostgreSQL row accepting closure to handle results, if any.
/// - returns: A future that signals query completion.
public func query(_ query: PostgreSQLQuery, _ onRow: @escaping ([PostgreSQLColumn: PostgreSQLData]) throws -> ()) -> Future<Void> {
return self.query(query, resultFormat: .binary, onRow)
}
/// Runs a parameterized `Query`, returning each row of the results to the supplied handler one at a time.
///
/// try conn.query(.select(.all, from: "users")) { row in
/// print(row)
/// }
///
/// Any values bound to the `DataQuery` as placeholders will be sent as query parameters.
///
/// - parameters:
/// - query: `Query` to execute.
/// - resultFormat: Desired `PostgreSQLResultFormat` to request from PostgreSQL. Defaults to `.binary`.
/// - onRow: PostgreSQL row accepting closure to handle results, if any.
/// - returns: A future that signals query completion.
public func query(_ query: PostgreSQLQuery, resultFormat: PostgreSQLResultFormat, _ onRow: @escaping ([PostgreSQLColumn: PostgreSQLData]) throws -> ()) -> Future<Void> {
var binds: [Encodable] = []
let sql = query.serialize(&binds)
return operation {
do {
return try self._query(sql, binds.map { try PostgreSQLDataEncoder().encode($0) }, resultFormat: resultFormat, onRow)
} catch {
return self.eventLoop.newFailedFuture(error: error)
}
}
}
// MARK: Private
/// Non-operation bounded query.
private func _query(_ string: String, _ parameters: [PostgreSQLData] = [], resultFormat: PostgreSQLResultFormat, _ onRow: @escaping ([PostgreSQLColumn: PostgreSQLData]) throws -> ()) throws -> Future<Void> {
logger?.record(query: string, values: parameters.map { $0.description })
var currentRow: PostgreSQLMessage.RowDescription?
return self.send([
.parse(.init(statementName: "", query: string, parameterTypes: parameters.map { $0.type })),
.describe(.init(command: .statement, name: "")),
.bind(.init(
portalName: "",
statementName: "",
parameterFormatCodes: parameters.map {
switch $0.storage {
case .text: return .text
case .binary, .null: return .binary
}
},
parameters: parameters.map {
switch $0.storage {
case .text(let string): return .init(data: Data(string.utf8))
case .binary(let data): return .init(data: data)
case .null: return .init(data: nil)
}
},
resultFormatCodes: resultFormat.formatCodes
)),
.execute(.init(portalName: "", maxRows: 0)),
.sync
]) { message in
switch message {
case .parseComplete: break
case .parameterDescription: break
case .noData: break
case .bindComplete: break
case .rowDescription(let row): currentRow = row
case .dataRow(let data):
guard let row = currentRow else {
throw PostgreSQLError(identifier: "query", reason: "Unexpected `PostgreSQLDataRow` without preceding `PostgreSQLRowDescription`.")
}
let parsed = try row.parse(data: data, formatCodes: resultFormat.formatCodes)
try onRow(parsed)
case .close: break
default: throw PostgreSQLError(identifier: "query", reason: "Unexpected message during `PostgreSQLParseRequest`: \(message)")
}
}
}
}