Skip to content

Commit

Permalink
Fix multiple array type mapping mistakes and add missing date and tim…
Browse files Browse the repository at this point in the history
…e array types (#463)

* Add missing definitions for Postgres type OIDs 1182 and 1183 (_date and _time), fix typos in the `macaddr8Array` and `datemultirange` types, and add missing array mappings for `timestamp` and `tstzrange`.

* Add PostgresArrayCodable conformance for Date

* Add tests for date arrays.

* Fix test to account for rounding error in conversion to days during Postgres encoding
  • Loading branch information
gwynne committed Mar 8, 2024
1 parent dc9caf8 commit b6496eb
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 7 deletions.
30 changes: 23 additions & 7 deletions Sources/PostgresNIO/Data/PostgresDataType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,14 @@ public struct PostgresDataType: RawRepresentable, Sendable, Hashable, CustomStri
/// `774`
public static let macaddr8 = PostgresDataType(774)
/// `775`
public static let macaddr8Aray = PostgresDataType(775)
@available(*, deprecated, renamed: "macaddr8Array")
public static let macaddr8Aray = Self.macaddr8Array
public static let macaddr8Array = PostgresDataType(775)
/// `790`
public static let money = PostgresDataType(790)
/// `791`
@available(*, deprecated, renamed: "moneyArray")
public static let _money = PostgresDataType(791)
public static let _money = Self.moneyArray
public static let moneyArray = PostgresDataType(791)
/// `829`
public static let macaddr = PostgresDataType(829)
Expand Down Expand Up @@ -192,6 +194,10 @@ public struct PostgresDataType: RawRepresentable, Sendable, Hashable, CustomStri
public static let timestamp = PostgresDataType(1114)
/// `1115` _timestamp
public static let timestampArray = PostgresDataType(1115)
/// `1182`
public static let dateArray = PostgresDataType(1182)
/// `1183`
public static let timeArray = PostgresDataType(1183)
/// `1184`
public static let timestamptz = PostgresDataType(1184)
/// `1185`
Expand Down Expand Up @@ -446,7 +452,7 @@ public struct PostgresDataType: RawRepresentable, Sendable, Hashable, CustomStri
case .circle: return "CIRCLE"
case .circleArray: return "CIRCLE[]"
case .macaddr8: return "MACADDR8"
case .macaddr8Aray: return "MACADDR8[]"
case .macaddr8Array: return "MACADDR8[]"
case .money: return "MONEY"
case .moneyArray: return "MONEY[]"
case .macaddr: return "MACADDR"
Expand Down Expand Up @@ -485,6 +491,8 @@ public struct PostgresDataType: RawRepresentable, Sendable, Hashable, CustomStri
case .time: return "TIME"
case .timestamp: return "TIMESTAMP"
case .timestampArray: return "TIMESTAMP[]"
case .dateArray: return "DATE[]"
case .timeArray: return "TIME[]"
case .timestamptz: return "TIMESTAMPTZ"
case .timestamptzArray: return "TIMESTAMPTZ[]"
case .interval: return "INTERVAL"
Expand Down Expand Up @@ -596,7 +604,7 @@ public struct PostgresDataType: RawRepresentable, Sendable, Hashable, CustomStri
case .line: return .lineArray
case .cidr: return .cidrArray
case .circle: return .circleArray
case .macaddr8Aray: return .macaddr8
case .macaddr8: return .macaddr8Array
case .money: return .moneyArray
case .int2vector: return .int2vectorArray
case .regproc: return .regprocArray
Expand All @@ -613,6 +621,9 @@ public struct PostgresDataType: RawRepresentable, Sendable, Hashable, CustomStri
case .aclitem: return .aclitemArray
case .macaddr: return .macaddrArray
case .inet: return .inetArray
case .timestamp: return .timestampArray
case .date: return .dateArray
case .time: return .timeArray
case .timestamptz: return .timestamptzArray
case .interval: return .intervalArray
case .numeric: return .numericArray
Expand All @@ -635,6 +646,7 @@ public struct PostgresDataType: RawRepresentable, Sendable, Hashable, CustomStri
case .regdictionary: return .regdictionaryArray
case .numrange: return .numrangeArray
case .tsrange: return .tsrangeArray
case .tstzrange: return .tstzrangeArray
case .daterange: return .daterangeArray
case .jsonpath: return .jsonpathArray
case .regnamespace: return .regnamespaceArray
Expand All @@ -643,7 +655,7 @@ public struct PostgresDataType: RawRepresentable, Sendable, Hashable, CustomStri
case .int4multirange: return .int4multirangeArray
case .tsmultirange: return .tsmultirangeArray
case .tstzmultirange: return .tstzmultirangeArray
case .datemultirange: return .datemultirange
case .datemultirange: return .datemultirangeArray
case .int8multirange: return .int8multirangeArray
case .bool: return .boolArray
case .bytea: return .byteaArray
Expand Down Expand Up @@ -677,7 +689,7 @@ public struct PostgresDataType: RawRepresentable, Sendable, Hashable, CustomStri
case .lineArray: return .line
case .cidrArray: return .cidr
case .circleArray: return .circle
case .macaddr8: return .macaddr8Aray
case .macaddr8Array: return .macaddr8
case .moneyArray: return .money
case .int2vectorArray: return .int2vector
case .regprocArray: return .regproc
Expand All @@ -694,6 +706,9 @@ public struct PostgresDataType: RawRepresentable, Sendable, Hashable, CustomStri
case .aclitemArray: return .aclitem
case .macaddrArray: return .macaddr
case .inetArray: return .inet
case .timestampArray: return .timestamp
case .dateArray: return .date
case .timeArray: return .time
case .timestamptzArray: return .timestamptz
case .intervalArray: return .interval
case .numericArray: return .numeric
Expand All @@ -716,6 +731,7 @@ public struct PostgresDataType: RawRepresentable, Sendable, Hashable, CustomStri
case .regdictionaryArray: return .regdictionary
case .numrangeArray: return .numrange
case .tsrangeArray: return .tsrange
case .tstzrangeArray: return .tstzrange
case .daterangeArray: return .daterange
case .jsonpathArray: return .jsonpath
case .regnamespaceArray: return .regnamespace
Expand All @@ -724,7 +740,7 @@ public struct PostgresDataType: RawRepresentable, Sendable, Hashable, CustomStri
case .int4multirangeArray: return .int4multirange
case .tsmultirangeArray: return .tsmultirange
case .tstzmultirangeArray: return .tstzmultirange
case .datemultirange: return .datemultirange
case .datemultirangeArray: return .datemultirange
case .int8multirangeArray: return .int8multirange
case .boolArray: return .bool
case .byteaArray: return .bytea
Expand Down
7 changes: 7 additions & 0 deletions Sources/PostgresNIO/New/Data/Array+PostgresCodable.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import NIOCore
import struct Foundation.Date
import struct Foundation.UUID

// MARK: Protocols
Expand Down Expand Up @@ -85,6 +86,12 @@ extension UUID: PostgresArrayEncodable {
public static var psqlArrayType: PostgresDataType { .uuidArray }
}

extension Date: PostgresArrayDecodable {}

extension Date: PostgresArrayEncodable {
public static var psqlArrayType: PostgresDataType { .timestamptzArray }
}

extension Range: PostgresArrayDecodable where Bound: PostgresRangeArrayDecodable {}

extension Range: PostgresArrayEncodable where Bound: PostgresRangeArrayEncodable {
Expand Down
38 changes: 38 additions & 0 deletions Tests/IntegrationTests/PostgresNIOTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,44 @@ final class PostgresNIOTests: XCTestCase {
XCTAssertEqual(row?[data: "array"].array(of: Int64?.self), [1, nil, 3])
}

@available(*, deprecated, message: "Testing deprecated functionality")
func testDateArraySerialize() {
var conn: PostgresConnection?
XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait())
defer { XCTAssertNoThrow( try conn?.close().wait() ) }
let date1 = Date(timeIntervalSince1970: 1704088800),
date2 = Date(timeIntervalSince1970: 1706767200),
date3 = Date(timeIntervalSince1970: 1709272800)
var rows: PostgresQueryResult?
XCTAssertNoThrow(rows = try conn?.query("""
select
$1::timestamptz[] as array
""", [
PostgresData(array: [date1, date2, date3])
]).wait())
let row = rows?.first?.makeRandomAccess()
XCTAssertEqual(row?[data: "array"].array(of: Date.self), [date1, date2, date3])
}

@available(*, deprecated, message: "Testing deprecated functionality")
func testDateArraySerializeAsPostgresDate() {
var conn: PostgresConnection?
XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait())
defer { XCTAssertNoThrow(try conn?.close().wait()) }
let date1 = Date(timeIntervalSince1970: 1704088800),//8766
date2 = Date(timeIntervalSince1970: 1706767200),//8797
date3 = Date(timeIntervalSince1970: 1709272800) //8826
var data = PostgresData(array: [date1, date2, date3].map { Int32(($0.timeIntervalSince1970 - 946_684_800) / 86_400).postgresData }, elementType: .date)
data.type = .dateArray // N.B.: `.date` format is an Int32 count of days since psqlStartDate
var rows: PostgresQueryResult?
XCTAssertNoThrow(rows = try conn?.query("select $1::date[] as array", [data]).wait())
let row = rows?.first?.makeRandomAccess()
XCTAssertEqual(
row?[data: "array"].array(of: Date.self)?.map { Int32((($0.timeIntervalSince1970 - 946_684_800) / 86_400).rounded(.toNearestOrAwayFromZero)) },
[date1, date2, date3].map { Int32((($0.timeIntervalSince1970 - 946_684_800) / 86_400).rounded(.toNearestOrAwayFromZero)) }
)
}

// https://github.com/vapor/postgres-nio/issues/143
func testEmptyStringFromNonNullColumn() {
var conn: PostgresConnection?
Expand Down
4 changes: 4 additions & 0 deletions Tests/PostgresNIOTests/New/Data/Array+PSQLCodableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ class Array_PSQLCodableTests: XCTestCase {
XCTAssertEqual(UUID.psqlType, .uuid)
XCTAssertEqual([UUID].psqlType, .uuidArray)

XCTAssertEqual(Date.psqlArrayType, .timestamptzArray)
XCTAssertEqual(Date.psqlType, .timestamptz)
XCTAssertEqual([Date].psqlType, .timestamptzArray)

XCTAssertEqual(Range<Int32>.psqlArrayType, .int4RangeArray)
XCTAssertEqual(Range<Int32>.psqlType, .int4Range)
XCTAssertEqual([Range<Int32>].psqlType, .int4RangeArray)
Expand Down

0 comments on commit b6496eb

Please sign in to comment.