|
1 | 1 | import Async
|
2 | 2 |
|
3 | 3 | extension PostgreSQLClient {
|
4 |
| - /// Sends a simple PostgreSQL query command, collecting the parsed results. |
5 |
| - public func query(_ string: String) -> Future<[[String: PostgreSQLData]]> { |
| 4 | + /// Sends a parameterized PostgreSQL query command, collecting the parsed results. |
| 5 | + public func query( |
| 6 | + _ string: String, |
| 7 | + _ parameters: [PostgreSQLData] = [] |
| 8 | + ) throws -> Future<[[String: PostgreSQLData]]> { |
6 | 9 | var rows: [[String: PostgreSQLData]] = []
|
7 |
| - return query(string) { row in |
| 10 | + return try query(string, parameters) { row in |
8 | 11 | rows.append(row)
|
9 |
| - }.map(to: [[String: PostgreSQLData]].self) { |
10 |
| - return rows |
| 12 | + }.map(to: [[String: PostgreSQLData]].self) { |
| 13 | + return rows |
11 | 14 | }
|
12 | 15 | }
|
13 | 16 |
|
14 |
| - /// Sends a simple PostgreSQL query command, returning the parsed results to |
| 17 | + /// Sends a parameterized PostgreSQL query command, returning the parsed results to |
15 | 18 | /// the supplied closure.
|
16 |
| - public func query(_ string: String, onRow: @escaping ([String: PostgreSQLData]) -> ()) -> Future<Void> { |
| 19 | + public func query( |
| 20 | + _ string: String, |
| 21 | + _ parameters: [PostgreSQLData] = [], |
| 22 | + onRow: @escaping ([String: PostgreSQLData]) -> () |
| 23 | + ) throws -> Future<Void> { |
| 24 | + let parse = PostgreSQLParseRequest( |
| 25 | + statementName: "", |
| 26 | + query: string, |
| 27 | + parameterTypes: parameters.map { .type(forData: $0) } |
| 28 | + ) |
| 29 | + let describe = PostgreSQLDescribeRequest(type: .statement, name: "") |
17 | 30 | var currentRow: PostgreSQLRowDescription?
|
18 |
| - let query = PostgreSQLQuery(query: string) |
19 |
| - return send([.query(query)]) { message in |
| 31 | + var currentParameters: PostgreSQLParameterDescription? |
| 32 | + return send([ |
| 33 | + .parse(parse), .describe(describe), .sync |
| 34 | + ]) { message in |
20 | 35 | switch message {
|
21 |
| - case .rowDescription(let row): |
22 |
| - currentRow = row |
23 |
| - case .dataRow(let data): |
24 |
| - let row = currentRow !! "Unexpected PostgreSQLDataRow without preceding PostgreSQLRowDescription." |
25 |
| - let parsed = try row.parse(data: data, formats: row.fields.map { $0.formatCode }) |
26 |
| - onRow(parsed) |
27 |
| - case .close: break // query over, waiting for `readyForQuery` |
28 |
| - default: fatalError("Unexpected message during PostgreSQLQuery: \(message)") |
| 36 | + case .parseComplete: break |
| 37 | + case .rowDescription(let row): currentRow = row |
| 38 | + case .parameterDescription(let parameters): currentParameters = parameters |
| 39 | + case .noData: break |
| 40 | + default: fatalError("Unexpected message during PostgreSQLParseRequest: \(message)") |
| 41 | + } |
| 42 | + }.flatMap(to: Void.self) { |
| 43 | + let parameterDataTypes = (currentParameters !! "Unexpected nil parameters").dataTypes |
| 44 | + let resultDataTypes = currentRow?.fields.map { $0.dataType } ?? [] // nil currentRow means no resutls |
| 45 | + |
| 46 | + // cache so we don't compute twice |
| 47 | + let _parameterFormats = parameterDataTypes.map { $0.preferredFormat } |
| 48 | + let _resultFormats = resultDataTypes.map { $0.preferredFormat } |
| 49 | + let bind = try PostgreSQLBindRequest( |
| 50 | + portalName: "", |
| 51 | + statementName: "", |
| 52 | + parameterFormatCodes: _parameterFormats, |
| 53 | + parameters: parameters.enumerated().map { try .make(data: $0.1, format: _parameterFormats[$0.0]) }, |
| 54 | + resultFormatCodes: _resultFormats |
| 55 | + ) |
| 56 | + let execute = PostgreSQLExecuteRequest( |
| 57 | + portalName: "", |
| 58 | + maxRows: 0 |
| 59 | + ) |
| 60 | + return self.send([ |
| 61 | + .bind(bind), .execute(execute), .sync |
| 62 | + ]) { message in |
| 63 | + switch message { |
| 64 | + case .bindComplete: break |
| 65 | + case .dataRow(let data): |
| 66 | + let row = currentRow !! "Unexpected PostgreSQLDataRow without preceding PostgreSQLRowDescription." |
| 67 | + let parsed = try row.parse(data: data, formats: _resultFormats) |
| 68 | + onRow(parsed) |
| 69 | + case .close: break |
| 70 | + case .noData: break |
| 71 | + default: fatalError("Unexpected message during PostgreSQLParseRequest: \(message)") |
| 72 | + } |
29 | 73 | }
|
30 | 74 | }
|
31 | 75 | }
|
32 | 76 | }
|
| 77 | + |
| 78 | +/// MARK: Codable |
| 79 | + |
| 80 | +extension PostgreSQLClient { |
| 81 | + /// Sends a parameterized PostgreSQL query command, collecting the parsed results. |
| 82 | + public func query( |
| 83 | + _ string: String, |
| 84 | + encoding parameters: [Encodable] |
| 85 | + ) throws -> Future<[[String: PostgreSQLData]]> { |
| 86 | + let parameters = try parameters.map { try PostgreSQLDataEncoder().encode($0) } |
| 87 | + return try query(string, parameters) |
| 88 | + } |
| 89 | + |
| 90 | + /// Sends a parameterized PostgreSQL query command, returning the parsed results to |
| 91 | + /// the supplied closure. |
| 92 | + public func query( |
| 93 | + _ string: String, |
| 94 | + encoding parameters: [Encodable], |
| 95 | + onRow: @escaping ([String: PostgreSQLData]) -> () |
| 96 | + ) throws -> Future<Void> { |
| 97 | + let parameters = try parameters.map { try PostgreSQLDataEncoder().encode($0) } |
| 98 | + return try query(string, parameters, onRow: onRow) |
| 99 | + } |
| 100 | +} |
0 commit comments