@@ -7,14 +7,73 @@ extension String: PostgreSQLDataCustomConvertible {
7
7
/// See `PostgreSQLDataCustomConvertible.convertFromPostgreSQLData(_:)`
8
8
public static func convertFromPostgreSQLData( _ data: PostgreSQLData ) throws -> String {
9
9
guard let value = data. data else {
10
- throw PostgreSQLError ( identifier: " data " , reason: " Could not decode String from `null` data. " )
10
+ throw PostgreSQLError ( identifier: " string " , reason: " Could not decode String from `null` data. " )
11
11
}
12
12
switch data. format {
13
- case . text: return String ( data: value, encoding: . utf8) !! " Non-utf8 "
13
+ case . text:
14
+ guard let string = String ( data: value, encoding: . utf8) else {
15
+ throw PostgreSQLError ( identifier: " string " , reason: " Non-UTF8 string: \( value. hexDebug) . " )
16
+ }
17
+ return string
14
18
case . binary:
15
19
switch data. type {
16
- case . text, . name: return String ( data: value, encoding: . utf8) !! " Non-utf8 "
17
- default : throw PostgreSQLError ( identifier: " data " , reason: " Could not decode String from data type: \( data. type) " )
20
+ case . text, . name, . varchar, . bpchar:
21
+ guard let string = String ( data: value, encoding: . utf8) else {
22
+ throw PostgreSQLError ( identifier: " string " , reason: " Non-UTF8 string: \( value. hexDebug) . " )
23
+ }
24
+ return string
25
+ case . point:
26
+ let point = try PostgreSQLPoint . convertFromPostgreSQLData ( data)
27
+ return point. description
28
+ case . numeric:
29
+ /// create mutable value since we will be using `.extract` which advances the buffer's view
30
+ var value = value
31
+
32
+ /// grab the numeric metadata from the beginning of the array
33
+ let metadata = value. extract ( PostgreSQLNumericMetadata . self)
34
+
35
+ var integer = " "
36
+ var fractional = " "
37
+ for offset in 0 ..< metadata. ndigits. bigEndian {
38
+ /// extract current char and advance memory
39
+ let char = value. extract ( Int16 . self) . bigEndian
40
+
41
+ /// conver the current char to its string form
42
+ let string : String
43
+ if char == 0 {
44
+ /// 0 means 4 zeros
45
+ string = " 0000 "
46
+ } else {
47
+ string = char. description
48
+ }
49
+
50
+ /// depending on our offset, append the string to before or after the decimal point
51
+ if offset < metadata. weight. bigEndian + 1 {
52
+ integer += string
53
+ } else {
54
+ fractional += string
55
+ }
56
+ }
57
+
58
+ /// use the dscale to remove extraneous zeroes at the end of the fractional part
59
+ let lastSignificantIndex = fractional. index ( fractional. startIndex, offsetBy: Int ( metadata. dscale. bigEndian) )
60
+ fractional = String ( fractional [ ..< lastSignificantIndex] )
61
+
62
+ /// determine whether fraction is empty and dynamically add `.`
63
+ let numeric : String
64
+ if fractional != " " {
65
+ numeric = integer + " . " + fractional
66
+ } else {
67
+ numeric = integer
68
+ }
69
+
70
+ /// use sign to determine adding a leading `-`
71
+ if metadata. sign. bigEndian == 1 {
72
+ return " - " + numeric
73
+ } else {
74
+ return numeric
75
+ }
76
+ default : throw PostgreSQLError ( identifier: " string " , reason: " Could not decode String from binary data type: \( data. type) " )
18
77
}
19
78
}
20
79
}
@@ -25,11 +84,23 @@ extension String: PostgreSQLDataCustomConvertible {
25
84
}
26
85
}
27
86
87
+ /// Represents the meta information preceeding a numeric value.
88
+ struct PostgreSQLNumericMetadata {
89
+ /// The number of digits after this metadata
90
+ var ndigits : Int16
91
+ /// How many of the digits are before the decimal point (always add 1)
92
+ var weight : Int16
93
+ /// If 1, this number is negative. Otherwise, positive.
94
+ var sign : Int16
95
+ /// The number of sig digits after the decimal place (get rid of trailing 0s)
96
+ var dscale : Int16
97
+ }
98
+
28
99
extension Data {
29
100
/// Convert the row's data into a string, throwing if invalid encoding.
30
101
internal func makeString( encoding: String . Encoding = . utf8) throws -> String {
31
102
guard let string = String ( data: self , encoding: encoding) else {
32
- throw PostgreSQLError ( identifier: " utf8String " , reason: " Unexpected non-UTF8 string. " )
103
+ throw PostgreSQLError ( identifier: " utf8String " , reason: " Unexpected non-UTF8 string: \( hexDebug ) . " )
33
104
}
34
105
35
106
return string
0 commit comments