Skip to content

Commit 448f1ca

Browse files
authored
Merge pull request #207 from vapor/row-decoder-perf
improve MySQL row decoder performance
2 parents 8a2cbdd + 5e37bd9 commit 448f1ca

File tree

2 files changed

+25
-21
lines changed

2 files changed

+25
-21
lines changed

Sources/MySQL/Codable/MySQLRowDecoder.swift

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct MySQLRowDecoder {
2121
}
2222

2323
func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key : CodingKey {
24-
return .init(_KeyedDecodingContainer(row: row, table: table))
24+
return .init(_KeyedDecodingContainer(self))
2525
}
2626

2727
func unkeyedContainer() throws -> UnkeyedDecodingContainer {
@@ -34,36 +34,31 @@ struct MySQLRowDecoder {
3434
}
3535

3636
private struct _KeyedDecodingContainer<Key>: KeyedDecodingContainerProtocol where Key: CodingKey {
37-
let allKeys: [Key]
38-
let codingPath: [CodingKey] = []
39-
let row: [MySQLColumn: MySQLData]
40-
let table: String?
37+
var allKeys: [Key] {
38+
return []
39+
}
40+
var codingPath: [CodingKey] {
41+
return []
42+
}
43+
let decoder: _Decoder
4144

42-
init(row: [MySQLColumn: MySQLData], table: String?) {
43-
self.row = row
44-
self.table = table
45-
self.allKeys = row.keys.compactMap { col in
46-
if table == nil || col.table == table || col.table == nil {
47-
return col.name
48-
} else {
49-
return nil
50-
}
51-
}.compactMap(Key.init(stringValue:))
45+
init(_ decoder: _Decoder) {
46+
self.decoder = decoder
5247
}
5348

5449
func contains(_ key: Key) -> Bool {
55-
return allKeys.contains { $0.stringValue == key.stringValue }
50+
return true
5651
}
5752

5853
func decodeNil(forKey key: Key) throws -> Bool {
59-
guard let data = row.firstValue(forColumn: key.stringValue, inTable: table) else {
54+
guard let data = decoder.row.firstValue(forColumn: key.stringValue, inTable: decoder.table) else {
6055
return true
6156
}
6257
return data.isNull
6358
}
6459

6560
func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T : Decodable {
66-
guard let data = row.firstValue(forColumn: key.stringValue, inTable: table) else {
61+
guard let data = decoder.row.firstValue(forColumn: key.stringValue, inTable: decoder.table) else {
6762
throw DecodingError.valueNotFound(T.self, .init(codingPath: codingPath + [key], debugDescription: "Could not decode \(T.self)."))
6863
}
6964
return try MySQLDataDecoder().decode(T.self, from: data)

Sources/MySQL/Connection/MySQLColumn.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,18 @@ extension MySQLColumnDefinition41 {
5454
extension Dictionary where Key == MySQLColumn {
5555
/// Accesses the _first_ value from this dictionary with a matching field name.
5656
public func firstValue(forColumn columnName: String, inTable table: String? = nil) -> Value? {
57-
for (field, value) in self {
58-
if (table == nil || field.table == nil || field.table == table) && field.name == columnName {
59-
return value
57+
if table != nil, let specific = self[MySQLColumn(table: table, name: columnName)] {
58+
// check for column with matching table name
59+
return specific
60+
} else if let unspecific = self[MySQLColumn(table: nil, name: columnName)] {
61+
// check for column without table name
62+
return unspecific
63+
} else if table == nil {
64+
// check for column with table name where we don't specify one
65+
for (col, row) in self {
66+
if col.name == columnName {
67+
return row
68+
}
6069
}
6170
}
6271
return nil

0 commit comments

Comments
 (0)