diff --git a/Package.swift b/Package.swift index 5f6562f6..628b895b 100644 --- a/Package.swift +++ b/Package.swift @@ -8,10 +8,10 @@ let swiftSettings: [SwiftSetting] = [ let package = Package( name: "postgres-nio", platforms: [ - .macOS(.v10_15), - .iOS(.v13), - .watchOS(.v6), - .tvOS(.v13), + .macOS(.v13), + .iOS(.v16), + .watchOS(.v9), + .tvOS(.v16), ], products: [ .library(name: "PostgresNIO", targets: ["PostgresNIO"]), diff --git a/Sources/PostgresNIO/Connection/PostgresConnection+Configuration.swift b/Sources/PostgresNIO/Connection/PostgresConnection+Configuration.swift index b260723a..450bc5ac 100644 --- a/Sources/PostgresNIO/Connection/PostgresConnection+Configuration.swift +++ b/Sources/PostgresNIO/Connection/PostgresConnection+Configuration.swift @@ -246,7 +246,7 @@ extension PostgresConnection { } let connection: InternalConfiguration.Connection - let username: String? + let username: String let password: String? let database: String? var tls: Configuration.TLS diff --git a/Sources/PostgresNIO/Connection/PostgresConnection.swift b/Sources/PostgresNIO/Connection/PostgresConnection.swift index 229cd647..302d9c34 100644 --- a/Sources/PostgresNIO/Connection/PostgresConnection.swift +++ b/Sources/PostgresNIO/Connection/PostgresConnection.swift @@ -95,16 +95,9 @@ public final class PostgresConnection: @unchecked Sendable { return self.eventLoop.makeFailedFuture(error) } - let startupFuture: EventLoopFuture<Void> - if configuration.username == nil { - startupFuture = eventHandler.readyForStartupFuture - } else { - startupFuture = eventHandler.authenticateFuture - } - // 3. wait for startup future to succeed. - return startupFuture.flatMapError { error in + return eventHandler.authenticateFuture.flatMapError { error in // in case of an startup error, the connection must be closed and after that // the originating error should be surfaced @@ -170,7 +163,7 @@ public final class PostgresConnection: @unchecked Sendable { connectFuture = bootstrap.connect(unixDomainSocketPath: path) case .bootstrapped(let channel): guard channel.isActive else { - return eventLoop.makeFailedFuture(PSQLError.connectionError(underlying: ChannelError.alreadyClosed)) + return eventLoop.makeFailedFuture(PostgresError.connectionError(underlying: ChannelError.alreadyClosed)) } connectFuture = eventLoop.makeSucceededFuture(channel) } @@ -180,10 +173,10 @@ public final class PostgresConnection: @unchecked Sendable { return connection.start(configuration: configuration).map { _ in connection } }.flatMapErrorThrowing { error -> PostgresConnection in switch error { - case is PSQLError: + case is PostgresError: throw error default: - throw PSQLError.connectionError(underlying: error) + throw PostgresError.connectionError(underlying: error) } } } @@ -212,7 +205,7 @@ public final class PostgresConnection: @unchecked Sendable { var logger = logger logger[postgresMetadataKey: .connectionID] = "\(self.id)" guard query.binds.count <= Int(UInt16.max) else { - return self.channel.eventLoop.makeFailedFuture(PSQLError(code: .tooManyParameters, query: query)) + return self.channel.eventLoop.makeFailedFuture(PostgresError(code: .tooManyParameters, query: query)) } let promise = self.channel.eventLoop.makePromise(of: PSQLRowStream.self) @@ -247,7 +240,7 @@ public final class PostgresConnection: @unchecked Sendable { func execute(_ executeStatement: PSQLExecuteStatement, logger: Logger) -> EventLoopFuture<PSQLRowStream> { guard executeStatement.binds.count <= Int(UInt16.max) else { - return self.channel.eventLoop.makeFailedFuture(PSQLError(code: .tooManyParameters)) + return self.channel.eventLoop.makeFailedFuture(PostgresError(code: .tooManyParameters)) } let promise = self.channel.eventLoop.makePromise(of: PSQLRowStream.self) let context = ExtendedQueryContext( @@ -285,74 +278,6 @@ public final class PostgresConnection: @unchecked Sendable { extension PostgresConnection { static let idGenerator = ManagedAtomic(0) - - @available(*, deprecated, - message: "Use the new connect method that allows you to connect and authenticate in a single step", - renamed: "connect(on:configuration:id:logger:)" - ) - public static func connect( - to socketAddress: SocketAddress, - tlsConfiguration: TLSConfiguration? = nil, - serverHostname: String? = nil, - logger: Logger = .init(label: "codes.vapor.postgres"), - on eventLoop: EventLoop - ) -> EventLoopFuture<PostgresConnection> { - var tlsFuture: EventLoopFuture<PostgresConnection.Configuration.TLS> - - if let tlsConfiguration = tlsConfiguration { - tlsFuture = eventLoop.makeSucceededVoidFuture().flatMapBlocking(onto: .global(qos: .default)) { - try .require(.init(configuration: tlsConfiguration)) - } - } else { - tlsFuture = eventLoop.makeSucceededFuture(.disable) - } - - return tlsFuture.flatMap { tls in - var options = PostgresConnection.Configuration.Options() - options.tlsServerName = serverHostname - let configuration = PostgresConnection.InternalConfiguration( - connection: .resolved(address: socketAddress), - username: nil, - password: nil, - database: nil, - tls: tls, - options: options - ) - - return PostgresConnection.connect( - connectionID: self.idGenerator.wrappingIncrementThenLoad(ordering: .relaxed), - configuration: configuration, - logger: logger, - on: eventLoop - ) - }.flatMapErrorThrowing { error in - throw error.asAppropriatePostgresError - } - } - - @available(*, deprecated, - message: "Use the new connect method that allows you to connect and authenticate in a single step", - renamed: "connect(on:configuration:id:logger:)" - ) - public func authenticate( - username: String, - database: String? = nil, - password: String? = nil, - logger: Logger = .init(label: "codes.vapor.postgres") - ) -> EventLoopFuture<Void> { - let authContext = AuthContext( - username: username, - password: password, - database: database) - let outgoing = PSQLOutgoingEvent.authenticate(authContext) - self.channel.triggerUserOutboundEvent(outgoing, promise: nil) - - return self.channel.pipeline.handler(type: PSQLEventsHandler.self).flatMap { handler in - handler.authenticateFuture - }.flatMapErrorThrowing { error in - throw error.asAppropriatePostgresError - } - } } // MARK: Async/Await Interface @@ -417,7 +342,7 @@ extension PostgresConnection { logger[postgresMetadataKey: .connectionID] = "\(self.id)" guard query.binds.count <= Int(UInt16.max) else { - throw PSQLError(code: .tooManyParameters, query: query, file: file, line: line) + throw PostgresError(code: .tooManyParameters, query: query, file: file, line: line) } let promise = self.channel.eventLoop.makePromise(of: PSQLRowStream.self) let context = ExtendedQueryContext( @@ -430,7 +355,7 @@ extension PostgresConnection { do { return try await promise.futureResult.map({ $0.asyncSequence() }).get() - } catch var error as PSQLError { + } catch var error as PostgresError { error.file = file error.line = line error.query = query @@ -486,7 +411,7 @@ extension PostgresConnection { .map { $0.asyncSequence() } .get() .map { try preparedStatement.decodeRow($0) } - } catch var error as PSQLError { + } catch var error as PostgresError { error.file = file error.line = line error.query = .init( @@ -520,7 +445,7 @@ extension PostgresConnection { return try await promise.futureResult .map { $0.commandTag } .get() - } catch var error as PSQLError { + } catch var error as PostgresError { error.file = file error.line = line error.query = .init( @@ -532,199 +457,6 @@ extension PostgresConnection { } } -// MARK: EventLoopFuture interface - -extension PostgresConnection { - - /// Run a query on the Postgres server the connection is connected to and collect all rows. - /// - /// - Parameters: - /// - query: The ``PostgresQuery`` to run - /// - logger: The `Logger` to log into for the query - /// - file: The file, the query was started in. Used for better error reporting. - /// - line: The line, the query was started in. Used for better error reporting. - /// - Returns: An EventLoopFuture, that allows access to the future ``PostgresQueryResult``. - public func query( - _ query: PostgresQuery, - logger: Logger, - file: String = #fileID, - line: Int = #line - ) -> EventLoopFuture<PostgresQueryResult> { - self.queryStream(query, logger: logger).flatMap { rowStream in - rowStream.all().flatMapThrowing { rows -> PostgresQueryResult in - guard let metadata = PostgresQueryMetadata(string: rowStream.commandTag) else { - throw PSQLError.invalidCommandTag(rowStream.commandTag) - } - return PostgresQueryResult(metadata: metadata, rows: rows) - } - }.enrichPSQLError(query: query, file: file, line: line) - } - - /// Run a query on the Postgres server the connection is connected to and iterate the rows in a callback. - /// - /// - Note: This API does not support back-pressure. If you need back-pressure please use the query - /// API, that supports structured concurrency. - /// - Parameters: - /// - query: The ``PostgresQuery`` to run - /// - logger: The `Logger` to log into for the query - /// - file: The file, the query was started in. Used for better error reporting. - /// - line: The line, the query was started in. Used for better error reporting. - /// - onRow: A closure that is invoked for every row. - /// - Returns: An EventLoopFuture, that allows access to the future ``PostgresQueryMetadata``. - @preconcurrency - public func query( - _ query: PostgresQuery, - logger: Logger, - file: String = #fileID, - line: Int = #line, - _ onRow: @escaping @Sendable (PostgresRow) throws -> () - ) -> EventLoopFuture<PostgresQueryMetadata> { - self.queryStream(query, logger: logger).flatMap { rowStream in - rowStream.onRow(onRow).flatMapThrowing { () -> PostgresQueryMetadata in - guard let metadata = PostgresQueryMetadata(string: rowStream.commandTag) else { - throw PSQLError.invalidCommandTag(rowStream.commandTag) - } - return metadata - } - }.enrichPSQLError(query: query, file: file, line: line) - } -} - -// MARK: PostgresDatabase conformance - -extension PostgresConnection: PostgresDatabase { - public func send( - _ request: PostgresRequest, - logger: Logger - ) -> EventLoopFuture<Void> { - guard let command = request as? PostgresCommands else { - preconditionFailure("\(#function) requires an instance of PostgresCommands. This will be a compile-time error in the future.") - } - - let resultFuture: EventLoopFuture<Void> - - switch command { - case .query(let query, let onMetadata, let onRow): - resultFuture = self.queryStream(query, logger: logger).flatMap { stream in - return stream.onRow(onRow).map { _ in - onMetadata(PostgresQueryMetadata(string: stream.commandTag)!) - } - } - - case .queryAll(let query, let onResult): - resultFuture = self.queryStream(query, logger: logger).flatMap { rows in - return rows.all().map { allrows in - onResult(.init(metadata: PostgresQueryMetadata(string: rows.commandTag)!, rows: allrows)) - } - } - - case .prepareQuery(let request): - resultFuture = self.prepareStatement(request.query, with: request.name, logger: logger).map { - request.prepared = PreparedQuery(underlying: $0, database: self) - } - - case .executePreparedStatement(let preparedQuery, let binds, let onRow): - var bindings = PostgresBindings(capacity: binds.count) - binds.forEach { bindings.append($0) } - - let statement = PSQLExecuteStatement( - name: preparedQuery.underlying.name, - binds: bindings, - rowDescription: preparedQuery.underlying.rowDescription - ) - - resultFuture = self.execute(statement, logger: logger).flatMap { rows in - return rows.onRow(onRow) - } - } - - return resultFuture.flatMapErrorThrowing { error in - throw error.asAppropriatePostgresError - } - } - - @preconcurrency - public func withConnection<T>(_ closure: (PostgresConnection) -> EventLoopFuture<T>) -> EventLoopFuture<T> { - closure(self) - } -} - -internal enum PostgresCommands: PostgresRequest { - case query(PostgresQuery, - onMetadata: @Sendable (PostgresQueryMetadata) -> () = { _ in }, - onRow: @Sendable (PostgresRow) throws -> ()) - case queryAll(PostgresQuery, onResult: @Sendable (PostgresQueryResult) -> ()) - case prepareQuery(request: PrepareQueryRequest) - case executePreparedStatement(query: PreparedQuery, binds: [PostgresData], onRow: @Sendable (PostgresRow) throws -> ()) - - func respond(to message: PostgresMessage) throws -> [PostgresMessage]? { - fatalError("This function must not be called") - } - - func start() throws -> [PostgresMessage] { - fatalError("This function must not be called") - } - - func log(to logger: Logger) { - fatalError("This function must not be called") - } -} - -// MARK: Notifications - -/// Context for receiving NotificationResponse messages on a connection, used for PostgreSQL's `LISTEN`/`NOTIFY` support. -public final class PostgresListenContext: Sendable { - private let promise: EventLoopPromise<Void> - - var future: EventLoopFuture<Void> { - self.promise.futureResult - } - - init(promise: EventLoopPromise<Void>) { - self.promise = promise - } - - func cancel() { - self.promise.succeed() - } - - /// Detach this listener so it no longer receives notifications. Other listeners, including those for the same channel, are unaffected. `UNLISTEN` is not sent; you are responsible for issuing an `UNLISTEN` query yourself if it is appropriate for your application. - public func stop() { - self.promise.succeed() - } -} - -extension PostgresConnection { - /// Add a handler for NotificationResponse messages on a certain channel. This is used in conjunction with PostgreSQL's `LISTEN`/`NOTIFY` support: to listen on a channel, you add a listener using this method to handle the NotificationResponse messages, then issue a `LISTEN` query to instruct PostgreSQL to begin sending NotificationResponse messages. - @discardableResult - @preconcurrency - public func addListener( - channel: String, - handler notificationHandler: @Sendable @escaping (PostgresListenContext, PostgresMessage.NotificationResponse) -> Void - ) -> PostgresListenContext { - let listenContext = PostgresListenContext(promise: self.eventLoop.makePromise(of: Void.self)) - let id = self.internalListenID.loadThenWrappingIncrement(ordering: .relaxed) - - let listener = NotificationListener( - channel: channel, - id: id, - eventLoop: self.eventLoop, - context: listenContext, - closure: notificationHandler - ) - - let task = HandlerTask.startListening(listener) - self.channel.write(task, promise: nil) - - listenContext.future.whenComplete { _ in - let task = HandlerTask.cancelListening(channel, id) - self.channel.write(task, promise: nil) - } - - return listenContext - } -} - enum CloseTarget { case preparedStatement(String) case portal(String) @@ -733,7 +465,7 @@ enum CloseTarget { extension EventLoopFuture { func enrichPSQLError(query: PostgresQuery, file: String, line: Int) -> EventLoopFuture<Value> { return self.flatMapErrorThrowing { error in - if var error = error as? PSQLError { + if var error = error as? PostgresError { error.file = file error.line = line error.query = query diff --git a/Sources/PostgresNIO/Connection/PostgresDatabase+PreparedQuery.swift b/Sources/PostgresNIO/Connection/PostgresDatabase+PreparedQuery.swift deleted file mode 100644 index 56496172..00000000 --- a/Sources/PostgresNIO/Connection/PostgresDatabase+PreparedQuery.swift +++ /dev/null @@ -1,79 +0,0 @@ -import NIOCore -import NIOConcurrencyHelpers -import struct Foundation.UUID - -extension PostgresDatabase { - public func prepare(query: String) -> EventLoopFuture<PreparedQuery> { - let name = "nio-postgres-\(UUID().uuidString)" - let request = PrepareQueryRequest(query, as: name) - return self.send(PostgresCommands.prepareQuery(request: request), logger: self.logger).map { _ in - // we can force unwrap the prepared here, since in a success case it must be set - // in the send method of `PostgresDatabase`. We do this dirty trick to work around - // the fact that the send method only returns an `EventLoopFuture<Void>`. - // Eventually we should move away from the `PostgresDatabase.send` API. - request.prepared! - } - } - - @preconcurrency - public func prepare(query: String, handler: @Sendable @escaping (PreparedQuery) -> EventLoopFuture<[[PostgresRow]]>) -> EventLoopFuture<[[PostgresRow]]> { - prepare(query: query) - .flatMap { preparedQuery in - handler(preparedQuery) - .flatMap { results in - preparedQuery.deallocate().map { results } - } - } - } -} - - -public struct PreparedQuery: Sendable { - let underlying: PSQLPreparedStatement - let database: PostgresDatabase - - init(underlying: PSQLPreparedStatement, database: PostgresDatabase) { - self.underlying = underlying - self.database = database - } - - public func execute(_ binds: [PostgresData] = []) -> EventLoopFuture<[PostgresRow]> { - let rowsBoxed = NIOLockedValueBox([PostgresRow]()) - return self.execute(binds) { row in - rowsBoxed.withLockedValue { - $0.append(row) - } - }.map { rowsBoxed.withLockedValue { $0 } } - } - - @preconcurrency - public func execute(_ binds: [PostgresData] = [], _ onRow: @Sendable @escaping (PostgresRow) throws -> ()) -> EventLoopFuture<Void> { - let command = PostgresCommands.executePreparedStatement(query: self, binds: binds, onRow: onRow) - return self.database.send(command, logger: self.database.logger) - } - - public func deallocate() -> EventLoopFuture<Void> { - self.underlying.connection.close(.preparedStatement(self.underlying.name), logger: self.database.logger) - } -} - -final class PrepareQueryRequest: Sendable { - let query: String - let name: String - var prepared: PreparedQuery? { - get { - self._prepared.withLockedValue { $0 } - } - set { - self._prepared.withLockedValue { - $0 = newValue - } - } - } - let _prepared: NIOLockedValueBox<PreparedQuery?> = .init(nil) - - init(_ query: String, as name: String) { - self.query = query - self.name = name - } -} diff --git a/Sources/PostgresNIO/Data/PostgresData+Array.swift b/Sources/PostgresNIO/Data/PostgresData+Array.swift deleted file mode 100644 index 5d648db6..00000000 --- a/Sources/PostgresNIO/Data/PostgresData+Array.swift +++ /dev/null @@ -1,135 +0,0 @@ -import NIOCore - -extension PostgresData { - @available(*, deprecated, message: "Use ``PostgresQuery`` and ``PostgresBindings`` instead.") - public init<T>(array: [T]) where T: PostgresDataConvertible { - self.init( - array: array.map { $0.postgresData }, - elementType: T.postgresDataType - ) - } - - public init(array: [PostgresData?], elementType: PostgresDataType) { - var buffer = ByteBufferAllocator().buffer(capacity: 0) - // 0 if empty, 1 if not - buffer.writeInteger(array.isEmpty ? 0 : 1, as: UInt32.self) - // b - this gets ignored by psql - buffer.writeInteger(0, as: UInt32.self) - // array element type - buffer.writeInteger(elementType.rawValue) - - // continue if the array is not empty - if !array.isEmpty { - // length of array - buffer.writeInteger(numericCast(array.count), as: UInt32.self) - // dimensions - buffer.writeInteger(1, as: UInt32.self) - - for item in array { - if let item = item, var value = item.value { - buffer.writeInteger(numericCast(value.readableBytes), as: UInt32.self) - buffer.writeBuffer(&value) - } else { - buffer.writeInteger(-1, as: Int32.self) - } - } - } - - guard let arrayType = elementType.arrayType else { - fatalError("No array type for \(elementType)") - } - self.init( - type: arrayType, - typeModifier: nil, - formatCode: .binary, - value: buffer - ) - } - - @available(*, deprecated, message: "Use ``PostgresRow`` and ``PostgresDecodable`` instead.") - public func array<T>(of type: T.Type = T.self) -> [T]? where T: PostgresDataConvertible { - guard let array = self.array else { - return nil - } - var items: [T] = [] - for data in array { - guard let item = T(postgresData: data) else { - // if we fail to convert any data, fail the entire array - return nil - } - items.append(item) - } - return items - } - - public var array: [PostgresData]? { - guard case .binary = self.formatCode else { - return nil - } - guard var value = self.value else { - return nil - } - // ensures the data type is actually an array - guard self.type.elementType != nil else { - return nil - } - guard let isNotEmpty = value.readInteger(as: UInt32.self) else { - return nil - } - // b - guard let _ = value.readInteger(as: UInt32.self) else { - return nil - } - guard let type = value.readInteger(as: PostgresDataType.self) else { - return nil - } - guard isNotEmpty == 1 else { - return [] - } - guard let length = value.readInteger(as: UInt32.self) else { - return nil - } - assert(length >= 0, "Invalid length") - - guard let dimensions = value.readInteger(as: UInt32.self) else { - return nil - } - assert(dimensions == 1, "Multi-dimensional arrays not yet supported") - - var array: [PostgresData] = [] - while - let itemLength = value.readInteger(as: Int32.self) - { - let itemValue = itemLength == -1 ? nil : value.readSlice(length: numericCast(itemLength)) - let data = PostgresData( - type: type, - typeModifier: nil, - formatCode: self.formatCode, - value: itemValue - ) - array.append(data) - } - return array - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension Array: PostgresDataConvertible where Element: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { - guard let arrayType = Element.postgresDataType.arrayType else { - fatalError("No array type for \(Element.postgresDataType)") - } - return arrayType - } - - public init?(postgresData: PostgresData) { - guard let array = postgresData.array(of: Element.self) else { - return nil - } - self = array - } - - public var postgresData: PostgresData? { - return PostgresData(array: self) - } -} diff --git a/Sources/PostgresNIO/Data/PostgresData+Bool.swift b/Sources/PostgresNIO/Data/PostgresData+Bool.swift deleted file mode 100644 index 0b9f2738..00000000 --- a/Sources/PostgresNIO/Data/PostgresData+Bool.swift +++ /dev/null @@ -1,66 +0,0 @@ -import NIOCore - -extension PostgresData { - public init(bool: Bool) { - var buffer = ByteBufferAllocator().buffer(capacity: 1) - buffer.writeInteger(bool ? 1 : 0, as: UInt8.self) - self.init(type: .bool, formatCode: .binary, value: buffer) - } - - public var bool: Bool? { - guard var value = self.value else { - return nil - } - guard value.readableBytes == 1 else { - return nil - } - guard let byte = value.readInteger(as: UInt8.self) else { - return nil - } - - switch self.formatCode { - case .text: - switch byte { - case Character("t").asciiValue!: - return true - case Character("f").asciiValue!: - return false - default: - return nil - } - case .binary: - switch byte { - case 1: - return true - case 0: - return false - default: - return nil - } - } - } -} - -extension PostgresData: ExpressibleByBooleanLiteral { - public init(booleanLiteral value: Bool) { - self.init(bool: value) - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension Bool: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { - return .bool - } - - public var postgresData: PostgresData? { - return .init(bool: self) - } - - public init?(postgresData: PostgresData) { - guard let bool = postgresData.bool else { - return nil - } - self = bool - } -} diff --git a/Sources/PostgresNIO/Data/PostgresData+Bytes.swift b/Sources/PostgresNIO/Data/PostgresData+Bytes.swift deleted file mode 100644 index 5ec507cd..00000000 --- a/Sources/PostgresNIO/Data/PostgresData+Bytes.swift +++ /dev/null @@ -1,40 +0,0 @@ -import struct Foundation.Data -import NIOCore - -extension PostgresData { - public init<Bytes>(bytes: Bytes) - where Bytes: Sequence, Bytes.Element == UInt8 - { - var buffer = ByteBufferAllocator().buffer(capacity: 1) - buffer.writeBytes(bytes) - self.init(type: .bytea, formatCode: .binary, value: buffer) - } - - public var bytes: [UInt8]? { - guard var value = self.value else { - return nil - } - guard let bytes = value.readBytes(length: value.readableBytes) else { - return nil - } - return bytes - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension Data: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { - return .bytea - } - - public var postgresData: PostgresData? { - return .init(bytes: self) - } - - public init?(postgresData: PostgresData) { - guard let bytes = postgresData.bytes else { - return nil - } - self.init(bytes) - } -} diff --git a/Sources/PostgresNIO/Data/PostgresData+Date.swift b/Sources/PostgresNIO/Data/PostgresData+Date.swift deleted file mode 100644 index 6d730f25..00000000 --- a/Sources/PostgresNIO/Data/PostgresData+Date.swift +++ /dev/null @@ -1,60 +0,0 @@ -import struct Foundation.Date -import NIOCore - -extension PostgresData { - public init(date: Date) { - var buffer = ByteBufferAllocator().buffer(capacity: 0) - let seconds = date.timeIntervalSince(_psqlDateStart) * Double(_microsecondsPerSecond) - buffer.writeInteger(Int64(seconds)) - self.init(type: .timestamptz, value: buffer) - } - - public var date: Date? { - guard var value = self.value else { - return nil - } - - switch self.formatCode { - case .text: - return nil - case .binary: - switch self.type { - case .timestamp, .timestamptz: - let microseconds = value.readInteger(as: Int64.self)! - let seconds = Double(microseconds) / Double(_microsecondsPerSecond) - return Date(timeInterval: seconds, since: _psqlDateStart) - case .time, .timetz: - return nil - case .date: - let days = value.readInteger(as: Int32.self)! - let seconds = Int64(days) * _secondsInDay - return Date(timeInterval: Double(seconds), since: _psqlDateStart) - default: - return nil - } - } - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension Date: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { - return .timestamptz - } - - public init?(postgresData: PostgresData) { - guard let date = postgresData.date else { - return nil - } - self = date - } - - public var postgresData: PostgresData? { - return .init(date: self) - } -} - -// MARK: Private -private let _microsecondsPerSecond: Int64 = 1_000_000 -private let _secondsInDay: Int64 = 24 * 60 * 60 -private let _psqlDateStart = Date(timeIntervalSince1970: 946_684_800) // values are stored as seconds before or after midnight 2000-01-01 diff --git a/Sources/PostgresNIO/Data/PostgresData+Decimal.swift b/Sources/PostgresNIO/Data/PostgresData+Decimal.swift deleted file mode 100644 index 3af709e5..00000000 --- a/Sources/PostgresNIO/Data/PostgresData+Decimal.swift +++ /dev/null @@ -1,35 +0,0 @@ -import Foundation - -extension PostgresData { - public var decimal: Decimal? { - guard let string = self.string else { - return nil - } - guard let decimal = Decimal(string: string) else { - return nil - } - return decimal - } - - public init(decimal: Decimal) { - self.init(string: decimal.description) - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension Decimal: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { - return .numeric - } - - public init?(postgresData: PostgresData) { - guard let decimal = postgresData.decimal else { - return nil - } - self = decimal - } - - public var postgresData: PostgresData? { - return .init(numeric: PostgresNumeric(decimal: self)) - } -} diff --git a/Sources/PostgresNIO/Data/PostgresData+Double.swift b/Sources/PostgresNIO/Data/PostgresData+Double.swift deleted file mode 100644 index 2d7735ef..00000000 --- a/Sources/PostgresNIO/Data/PostgresData+Double.swift +++ /dev/null @@ -1,53 +0,0 @@ -import NIOCore - -extension PostgresData { - public init(double: Double) { - var buffer = ByteBufferAllocator().buffer(capacity: 0) - buffer.psqlWriteDouble(double) - self.init(type: .float8, formatCode: .binary, value: buffer) - } - - public var double: Double? { - guard var value = self.value else { - return nil - } - - switch self.formatCode { - case .binary: - switch self.type { - case .float4: - return value.psqlReadFloat() - .flatMap { Double($0) } - case .float8: - return value.psqlReadDouble() - case .numeric: - return self.numeric?.double - default: - return nil - } - case .text: - guard let string = self.string else { - return nil - } - return Double(string) - } - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension Double: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { - return .float8 - } - - public init?(postgresData: PostgresData) { - guard let double = postgresData.double else { - return nil - } - self = double - } - - public var postgresData: PostgresData? { - return .init(double: self) - } -} diff --git a/Sources/PostgresNIO/Data/PostgresData+Float.swift b/Sources/PostgresNIO/Data/PostgresData+Float.swift deleted file mode 100644 index 45430934..00000000 --- a/Sources/PostgresNIO/Data/PostgresData+Float.swift +++ /dev/null @@ -1,47 +0,0 @@ -extension PostgresData { - public init(float: Float) { - self.init(double: Double(float)) - } - - public var float: Float? { - guard var value = self.value else { - return nil - } - - switch self.formatCode { - case .binary: - switch self.type { - case .float4: - return value.psqlReadFloat() - case .float8: - return value.psqlReadDouble() - .flatMap { Float($0) } - default: - return nil - } - case .text: - guard let string = self.string else { - return nil - } - return Float(string) - } - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension Float: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { - return .float4 - } - - public init?(postgresData: PostgresData) { - guard let float = postgresData.float else { - return nil - } - self = float - } - - public var postgresData: PostgresData? { - return .init(float: self) - } -} diff --git a/Sources/PostgresNIO/Data/PostgresData+Int.swift b/Sources/PostgresNIO/Data/PostgresData+Int.swift deleted file mode 100644 index 5a97b3fb..00000000 --- a/Sources/PostgresNIO/Data/PostgresData+Int.swift +++ /dev/null @@ -1,264 +0,0 @@ -extension PostgresData { - public init(int value: Int) { - self.init(type: .int8, value: .init(integer: Int64(value))) - } - - public init(uint8 value: UInt8) { - self.init(type: .char, value: .init(integer: value)) - } - - public init(int16 value: Int16) { - self.init(type: .int2, value: .init(integer: value)) - } - - public init(int32 value: Int32) { - self.init(type: .int4, value: .init(integer: value)) - } - - public init(int64 value: Int64) { - self.init(type: .int8, value: .init(integer: value)) - } - - public var int: Int? { - guard var value = self.value else { - return nil - } - - switch self.formatCode { - case .binary: - switch self.type { - case .char, .bpchar: - guard value.readableBytes == 1 else { - return nil - } - return value.readInteger(as: UInt8.self).flatMap(Int.init) - case .int2: - assert(value.readableBytes == 2) - return value.readInteger(as: Int16.self).flatMap(Int.init) - case .int4, .regproc: - assert(value.readableBytes == 4) - return value.readInteger(as: Int32.self).flatMap(Int.init) - case .oid: - assert(value.readableBytes == 4) - return value.readInteger(as: UInt32.self).flatMap { Int(exactly: $0) } - case .int8: - assert(value.readableBytes == 8) - return value.readInteger(as: Int64.self).flatMap { Int(exactly: $0) } - default: - return nil - } - case .text: - guard let string = self.string else { - return nil - } - return Int(string) - } - } - - public var uint8: UInt8? { - guard var value = self.value else { - return nil - } - - switch self.formatCode { - case .binary: - switch self.type { - case .char, .bpchar: - guard value.readableBytes == 1 else { - return nil - } - return value.readInteger(as: UInt8.self) - default: - return nil - } - case .text: - guard let string = self.string else { - return nil - } - return UInt8(string) - } - } - - public var int16: Int16? { - guard var value = self.value else { - return nil - } - - switch self.formatCode { - case .binary: - switch self.type { - case .char, .bpchar: - guard value.readableBytes == 1 else { - return nil - } - return value.readInteger(as: UInt8.self) - .flatMap(Int16.init) - case .int2: - assert(value.readableBytes == 2) - return value.readInteger(as: Int16.self) - default: - return nil - } - case .text: - guard let string = self.string else { - return nil - } - return Int16(string) - } - } - - public var int32: Int32? { - guard var value = self.value else { - return nil - } - - switch self.formatCode { - case .binary: - switch self.type { - case .char, .bpchar: - guard value.readableBytes == 1 else { - return nil - } - return value.readInteger(as: UInt8.self) - .flatMap(Int32.init) - case .int2: - assert(value.readableBytes == 2) - return value.readInteger(as: Int16.self) - .flatMap(Int32.init) - case .int4, .regproc: - assert(value.readableBytes == 4) - return value.readInteger(as: Int32.self) - .flatMap(Int32.init) - default: - return nil - } - case .text: - guard let string = self.string else { - return nil - } - return Int32(string) - } - } - - public var int64: Int64? { - guard var value = self.value else { - return nil - } - - switch self.formatCode { - case .binary: - switch self.type { - case .char, .bpchar: - guard value.readableBytes == 1 else { - return nil - } - return value.readInteger(as: UInt8.self) - .flatMap(Int64.init) - case .int2: - assert(value.readableBytes == 2) - return value.readInteger(as: Int16.self) - .flatMap(Int64.init) - case .int4, .regproc: - assert(value.readableBytes == 4) - return value.readInteger(as: Int32.self) - .flatMap(Int64.init) - case .oid: - assert(value.readableBytes == 4) - assert(Int.bitWidth == 64) // or else overflow is possible - return value.readInteger(as: UInt32.self) - .flatMap(Int64.init) - case .int8: - assert(value.readableBytes == 8) - assert(Int.bitWidth == 64) - return value.readInteger(as: Int64.self) - default: - return nil - } - case .text: - guard let string = self.string else { - return nil - } - return Int64(string) - } - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension Int: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { .int8 } - - public init?(postgresData: PostgresData) { - guard let int = postgresData.int else { - return nil - } - self = int - } - - public var postgresData: PostgresData? { - .init(int: self) - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension UInt8: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { .char } - - public init?(postgresData: PostgresData) { - guard let uint8 = postgresData.uint8 else { - return nil - } - self = uint8 - } - - public var postgresData: PostgresData? { - .init(uint8: self) - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension Int16: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { .int2 } - - public init?(postgresData: PostgresData) { - guard let int16 = postgresData.int16 else { - return nil - } - self = int16 - } - - public var postgresData: PostgresData? { - .init(int16: self) - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension Int32: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { .int4 } - - public init?(postgresData: PostgresData) { - guard let int32 = postgresData.int32 else { - return nil - } - self = int32 - } - - public var postgresData: PostgresData? { - .init(int32: self) - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension Int64: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { .int8 } - - public init?(postgresData: PostgresData) { - guard let int64 = postgresData.int64 else { - return nil - } - self = int64 - } - - public var postgresData: PostgresData? { - .init(int64: self) - } -} diff --git a/Sources/PostgresNIO/Data/PostgresData+JSON.swift b/Sources/PostgresNIO/Data/PostgresData+JSON.swift deleted file mode 100644 index 53a2d84c..00000000 --- a/Sources/PostgresNIO/Data/PostgresData+JSON.swift +++ /dev/null @@ -1,59 +0,0 @@ -import struct Foundation.Data -import NIOCore - -extension PostgresData { - public init(json jsonData: Data) { - let jsonData = [UInt8](jsonData) - - var buffer = ByteBufferAllocator() - .buffer(capacity: jsonData.count) - buffer.writeBytes(jsonData) - self.init(type: .json, formatCode: .binary, value: buffer) - } - - public init<T>(json value: T) throws where T: Encodable { - let jsonData = try PostgresNIO._defaultJSONEncoder.encode(value) - self.init(json: jsonData) - } - - public var json: Data? { - guard var value = self.value else { - return nil - } - guard case .json = self.type else { - return nil - } - guard let data = value.readBytes(length: value.readableBytes) else { - return nil - } - return Data(data) - } - - public func json<T>(as type: T.Type) throws -> T? where T: Decodable { - guard let data = self.json else { - return nil - } - return try PostgresNIO._defaultJSONDecoder.decode(T.self, from: data) - } -} - -@available(*, deprecated, message: "This protocol is going to be replaced with ``PostgresEncodable`` and ``PostgresDecodable`` and conforming to ``Codable`` at the same time") -public protocol PostgresJSONCodable: Codable, PostgresDataConvertible { } - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension PostgresJSONCodable { - public static var postgresDataType: PostgresDataType { - return .json - } - - public var postgresData: PostgresData? { - return try? .init(json: self) - } - - public init?(postgresData: PostgresData) { - guard let value = try? postgresData.json(as: Self.self) else { - return nil - } - self = value - } -} diff --git a/Sources/PostgresNIO/Data/PostgresData+JSONB.swift b/Sources/PostgresNIO/Data/PostgresData+JSONB.swift deleted file mode 100644 index 0d5befa3..00000000 --- a/Sources/PostgresNIO/Data/PostgresData+JSONB.swift +++ /dev/null @@ -1,70 +0,0 @@ -import NIOCore -import struct Foundation.Data - -fileprivate let jsonBVersionBytes: [UInt8] = [0x01] - -extension PostgresData { - public init(jsonb jsonData: Data) { - let jsonBData = [UInt8](jsonData) - - var buffer = ByteBufferAllocator() - .buffer(capacity: jsonBVersionBytes.count + jsonBData.count) - buffer.writeBytes(jsonBVersionBytes) - buffer.writeBytes(jsonBData) - - self.init(type: .jsonb, formatCode: .binary, value: buffer) - } - - public init<T>(jsonb value: T) throws where T: Encodable { - let jsonData = try PostgresNIO._defaultJSONEncoder.encode(value) - self.init(jsonb: jsonData) - } - - public var jsonb: Data? { - guard var value = self.value else { - return nil - } - guard case .jsonb = self.type else { - return nil - } - - guard let versionBytes = value.readBytes(length: jsonBVersionBytes.count), [UInt8](versionBytes) == jsonBVersionBytes else { - return nil - } - - guard let data = value.readBytes(length: value.readableBytes) else { - return nil - } - - return Data(data) - } - - public func jsonb<T>(as type: T.Type) throws -> T? where T: Decodable { - guard let data = jsonb else { - return nil - } - - return try PostgresNIO._defaultJSONDecoder.decode(T.self, from: data) - } -} - -@available(*, deprecated, message: "This protocol is going to be replaced with ``PostgresEncodable`` and ``PostgresDecodable`` and conforming to ``Codable`` at the same time") -public protocol PostgresJSONBCodable: Codable, PostgresDataConvertible { } - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension PostgresJSONBCodable { - public static var postgresDataType: PostgresDataType { - return .jsonb - } - - public var postgresData: PostgresData? { - return try? .init(jsonb: self) - } - - public init?(postgresData: PostgresData) { - guard let value = try? postgresData.jsonb(as: Self.self) else { - return nil - } - self = value - } -} diff --git a/Sources/PostgresNIO/Data/PostgresData+Optional.swift b/Sources/PostgresNIO/Data/PostgresData+Optional.swift deleted file mode 100644 index 9468478a..00000000 --- a/Sources/PostgresNIO/Data/PostgresData+Optional.swift +++ /dev/null @@ -1,19 +0,0 @@ -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension Optional: PostgresDataConvertible where Wrapped: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { - return Wrapped.postgresDataType - } - - public init?(postgresData: PostgresData) { - self = Wrapped.init(postgresData: postgresData) - } - - public var postgresData: PostgresData? { - switch self { - case .some(let wrapped): - return wrapped.postgresData - case .none: - return .init(type: Wrapped.postgresDataType, value: nil) - } - } -} diff --git a/Sources/PostgresNIO/Data/PostgresData+RawRepresentable.swift b/Sources/PostgresNIO/Data/PostgresData+RawRepresentable.swift deleted file mode 100644 index 6cc8316a..00000000 --- a/Sources/PostgresNIO/Data/PostgresData+RawRepresentable.swift +++ /dev/null @@ -1,17 +0,0 @@ -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension RawRepresentable where Self.RawValue: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { - RawValue.postgresDataType - } - - public init?(postgresData: PostgresData) { - guard let rawValue = RawValue.init(postgresData: postgresData) else { - return nil - } - self.init(rawValue: rawValue) - } - - public var postgresData: PostgresData? { - self.rawValue.postgresData - } -} diff --git a/Sources/PostgresNIO/Data/PostgresData+Set.swift b/Sources/PostgresNIO/Data/PostgresData+Set.swift deleted file mode 100644 index ade48db1..00000000 --- a/Sources/PostgresNIO/Data/PostgresData+Set.swift +++ /dev/null @@ -1,17 +0,0 @@ -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension Set: PostgresDataConvertible where Element: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { - [Element].postgresDataType - } - - public init?(postgresData: PostgresData) { - guard let array = [Element](postgresData: postgresData) else { - return nil - } - self = Set(array) - } - - public var postgresData: PostgresData? { - [Element](self).postgresData - } -} diff --git a/Sources/PostgresNIO/Data/PostgresData+String.swift b/Sources/PostgresNIO/Data/PostgresData+String.swift deleted file mode 100644 index f38e2ab8..00000000 --- a/Sources/PostgresNIO/Data/PostgresData+String.swift +++ /dev/null @@ -1,113 +0,0 @@ -import NIOCore - -extension PostgresData { - public init(string: String) { - var buffer = ByteBufferAllocator().buffer(capacity: string.utf8.count) - buffer.writeString(string) - self.init(type: .text, formatCode: .binary, value: buffer) - } - - public var string: String? { - guard var value = self.value else { - return nil - } - switch self.formatCode { - case .binary: - switch self.type { - case .varchar, .text, .name: - guard let string = value.readString(length: value.readableBytes) else { - return nil - } - return string - case .numeric: - return self.numeric?.string - case .uuid: - return value.readUUIDBytes()!.uuidString - case .timestamp, .timestamptz, .date: - return self.date?.description - case .money: - assert(value.readableBytes == 8) - guard let int64 = value.getInteger(at: value.readerIndex, as: Int64.self) else { - return nil - } - let description = int64.description - switch description.count { - case 0: - return "0.00" - case 1: - return "0.0" + description - case 2: - return "0." + description - default: - let decimalIndex = description.index(description.endIndex, offsetBy: -2) - return description[description.startIndex..<decimalIndex] - + "." - + description[decimalIndex..<description.endIndex] - } - case .float4, .float8: - return self.double?.description - case .int2, .int4, .int8: - return self.int?.description - case .bpchar: - return value.readString(length: value.readableBytes) - default: - if self.type.isUserDefined { - // custom type - return value.readString(length: value.readableBytes) - } else { - return nil - } - } - case .text: - guard let string = value.readString(length: value.readableBytes) else { - return nil - } - return string - } - } - - public var character: Character? { - guard var value = self.value else { - return nil - } - - switch self.formatCode { - case .binary: - switch self.type { - case .bpchar: - guard let byte = value.readInteger(as: UInt8.self) else { - return nil - } - return Character(UnicodeScalar(byte)) - default: - return nil - } - case .text: - return nil - } - } -} - -extension PostgresData: ExpressibleByStringLiteral { - public init(stringLiteral value: String) { - self.init(string: value) - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension String: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { - return .text - } - - public var postgresData: PostgresData? { - return .init(string: self) - } - - public init?(postgresData: PostgresData) { - guard let string = postgresData.string else { - return nil - } - self = string - } -} diff --git a/Sources/PostgresNIO/Data/PostgresData+UUID.swift b/Sources/PostgresNIO/Data/PostgresData+UUID.swift deleted file mode 100644 index 7c2da080..00000000 --- a/Sources/PostgresNIO/Data/PostgresData+UUID.swift +++ /dev/null @@ -1,48 +0,0 @@ -import Foundation -import NIOCore - -extension PostgresData { - public init(uuid: UUID) { - var buffer = ByteBufferAllocator().buffer(capacity: 16) - buffer.writeUUIDBytes(uuid) - self.init(type: .uuid, formatCode: .binary, value: buffer) - } - - public var uuid: UUID? { - guard var value = self.value else { - return nil - } - - switch self.formatCode { - case .binary: - switch self.type { - case .uuid: - return value.readUUIDBytes() - case .varchar, .text: - return self.string.flatMap { UUID(uuidString: $0) } - default: - return nil - } - case .text: - return nil - } - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension UUID: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { - return .uuid - } - - public init?(postgresData: PostgresData) { - guard let uuid = postgresData.uuid else { - return nil - } - self = uuid - } - - public var postgresData: PostgresData? { - return .init(uuid: self) - } -} diff --git a/Sources/PostgresNIO/Data/PostgresData.swift b/Sources/PostgresNIO/Data/PostgresData.swift deleted file mode 100644 index d0be48eb..00000000 --- a/Sources/PostgresNIO/Data/PostgresData.swift +++ /dev/null @@ -1,121 +0,0 @@ -import NIOCore -import struct Foundation.UUID - -public struct PostgresData: Sendable { - public static var null: PostgresData { - return .init(type: .null) - } - - /// The object ID of the field's data type. - public var type: PostgresDataType - - /// The type modifier (see pg_attribute.atttypmod). The meaning of the modifier is type-specific. - public var typeModifier: Int32? - - /// The format code being used for the field. - /// Currently will be zero (text) or one (binary). - /// In a RowDescription returned from the statement variant of Describe, - /// the format code is not yet known and will always be zero. - public var formatCode: PostgresFormat - - public var value: ByteBuffer? - - public init(type: PostgresDataType, typeModifier: Int32? = nil, formatCode: PostgresFormat = .binary, value: ByteBuffer? = nil) { - self.type = type - self.typeModifier = typeModifier - self.formatCode = formatCode - self.value = value - } -} - -@available(*, deprecated, message: "Deprecating conformance to `CustomStringConvertible` as a first step of deprecating `PostgresData`. Please use `PostgresBindings` or `PostgresCell` instead.") -extension PostgresData: CustomStringConvertible { - public var description: String { - guard var value = self.value else { - return "<null>" - } - let description: String? - - switch self.type { - case .bool: - description = self.bool?.description - case .float4: - description = self.float?.description - case .float8, .numeric: - description = self.double?.description - case .int2: - description = self.int16?.description - case .int4, .regproc, .oid: - description = self.int32?.description - case .int8: - description = self.int64?.description - case .timestamp, .timestamptz, .date, .time, .timetz: - description = self.date?.description - case .text: - description = self.string?.debugDescription - case .uuid: - description = self.uuid?.description - case .json: - description = String(decoding: value.readableBytesView, as: UTF8.self) - case .jsonb: - var value = value - value.moveReaderIndex(forwardBy: 1) - description = String(decoding: value.readableBytesView, as: UTF8.self) - case .uuidArray: - description = self.array(of: UUID.self)?.description - case .int8Array: - description = self.array(of: Int.self)?.description - case .float8Array: - description = self.array(of: Double.self)?.description - case .float4Array: - description = self.array(of: Float.self)?.description - case .textArray: - description = self.array(of: String.self)?.description - case .jsonbArray: - description = self.array?.description - default: - if self.type.isUserDefined { - // custom type - description = value.readString(length: value.readableBytes) - } else { - description = nil - } - } - - if let description = description { - return description - } else { - let raw: String - switch self.formatCode { - case .text: - raw = (value.readString(length: value.readableBytes) ?? "") - .debugDescription - case .binary: - raw = "0x" + value.readableBytesView.hexdigest() - } - return "\(raw) (\(self.type))" - } - } -} - -@available(*, deprecated, message: "Deprecating conformance to `CustomDebugStringConvertible` as a first step of deprecating `PostgresData`. Please use `PostgresBindings` or `PostgresCell` instead.") -extension PostgresData: CustomDebugStringConvertible { - public var debugDescription: String { - return self.description - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresDataConvertible`, since it is deprecated.") -extension PostgresData: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { - fatalError("PostgresData cannot be statically represented as a single data type") - } - - public init?(postgresData: PostgresData) { - self = postgresData - } - - public var postgresData: PostgresData? { - return self - } -} diff --git a/Sources/PostgresNIO/Data/PostgresDataConvertible.swift b/Sources/PostgresNIO/Data/PostgresDataConvertible.swift deleted file mode 100644 index 675ed6fe..00000000 --- a/Sources/PostgresNIO/Data/PostgresDataConvertible.swift +++ /dev/null @@ -1,8 +0,0 @@ -import Foundation - -@available(*, deprecated, message: "This protocol is going to be replaced with ``PostgresEncodable`` and ``PostgresDecodable``") -public protocol PostgresDataConvertible { - static var postgresDataType: PostgresDataType { get } - init?(postgresData: PostgresData) - var postgresData: PostgresData? { get } -} diff --git a/Sources/PostgresNIO/Data/PostgresDataType.swift b/Sources/PostgresNIO/Data/PostgresDataType.swift index c3e4e747..dac41995 100644 --- a/Sources/PostgresNIO/Data/PostgresDataType.swift +++ b/Sources/PostgresNIO/Data/PostgresDataType.swift @@ -17,13 +17,6 @@ extension PostgresFormat: CustomStringConvertible { } } -// TODO: The Codable conformance does not make any sense. Let's remove this with next major break. -extension PostgresFormat: Codable {} - -// TODO: Renamed during 1.x. Remove this with next major break. -@available(*, deprecated, renamed: "PostgresFormat") -public typealias PostgresFormatCode = PostgresFormat - /// Data types and their raw OIDs. /// /// Use `select * from pg_type where oid = <idhere>` to look up more information for a given type. @@ -113,14 +106,10 @@ public struct PostgresDataType: RawRepresentable, Sendable, Hashable, CustomStri /// `774` public static let macaddr8 = PostgresDataType(774) /// `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 = Self.moneyArray public static let moneyArray = PostgresDataType(791) /// `829` public static let macaddr = PostgresDataType(829) @@ -784,13 +773,3 @@ public struct PostgresDataType: RawRepresentable, Sendable, Hashable, CustomStri return self.knownSQLName ?? "UNKNOWN \(self.rawValue)" } } - -// TODO: The Codable conformance does not make any sense. Let's remove this with next major break. -extension PostgresDataType: Codable {} - -// TODO: The ExpressibleByIntegerLiteral conformance does not make any sense and is not used anywhere. Remove with next major break. -extension PostgresDataType: ExpressibleByIntegerLiteral { - public init(integerLiteral value: UInt32) { - self.init(value) - } -} diff --git a/Sources/PostgresNIO/Data/PostgresData+Numeric.swift b/Sources/PostgresNIO/Data/PostgresNumeric.swift similarity index 90% rename from Sources/PostgresNIO/Data/PostgresData+Numeric.swift rename to Sources/PostgresNIO/Data/PostgresNumeric.swift index e736a61c..a0c1c5ea 100644 --- a/Sources/PostgresNIO/Data/PostgresData+Numeric.swift +++ b/Sources/PostgresNIO/Data/PostgresNumeric.swift @@ -223,33 +223,6 @@ public struct PostgresNumeric: CustomStringConvertible, CustomDebugStringConvert } } -extension PostgresData { - public init(numeric: PostgresNumeric) { - var buffer = ByteBufferAllocator().buffer(capacity: 0) - buffer.writeInteger(numeric.ndigits, endianness: .big) - buffer.writeInteger(numeric.weight, endianness: .big) - buffer.writeInteger(numeric.sign, endianness: .big) - buffer.writeInteger(numeric.dscale, endianness: .big) - var value = numeric.value - buffer.writeBuffer(&value) - self.init(type: .numeric, value: buffer) - } - - public var numeric: PostgresNumeric? { - /// create mutable value since we will be using `.extract` which advances the buffer's view - guard var value = self.value else { - return nil - } - - /// grab the numeric metadata from the beginning of the array - guard let metadata = PostgresNumeric(buffer: &value) else { - return nil - } - - return metadata - } -} - private extension Collection { // splits the collection into chunks of the supplied size // if the collection is not evenly divisible, the last chunk will be smaller diff --git a/Sources/PostgresNIO/Data/PostgresRow.swift b/Sources/PostgresNIO/Data/PostgresRow.swift index e3aea692..401be2c6 100644 --- a/Sources/PostgresNIO/Data/PostgresRow.swift +++ b/Sources/PostgresNIO/Data/PostgresRow.swift @@ -183,28 +183,6 @@ extension PostgresRandomAccessRow: Sendable, RandomAccessCollection { } } -extension PostgresRandomAccessRow { - public subscript(data index: Int) -> PostgresData { - guard index < self.endIndex else { - preconditionFailure("index out of bounds") - } - let column = self.columns[index] - return PostgresData( - type: column.dataType, - typeModifier: column.dataTypeModifier, - formatCode: .binary, - value: self.cells[index] - ) - } - - public subscript(data column: String) -> PostgresData { - guard let index = self.lookupTable[column] else { - fatalError(#"A column "\#(column)" does not exist."#) - } - return self[data: index] - } -} - extension PostgresRandomAccessRow { /// Access the data in the provided column and decode it into the target type. /// @@ -261,64 +239,3 @@ extension PostgresRandomAccessRow { } } } - -// MARK: Deprecated API - -extension PostgresRow { - @available(*, deprecated, message: "Will be removed from public API.") - public var rowDescription: PostgresMessage.RowDescription { - let fields = self.columns.map { column in - PostgresMessage.RowDescription.Field( - name: column.name, - tableOID: UInt32(column.tableOID), - columnAttributeNumber: column.columnAttributeNumber, - dataType: PostgresDataType(UInt32(column.dataType.rawValue)), - dataTypeSize: column.dataTypeSize, - dataTypeModifier: column.dataTypeModifier, - formatCode: .init(psqlFormatCode: column.format) - ) - } - return PostgresMessage.RowDescription(fields: fields) - } - - @available(*, deprecated, message: "Iterate the cells on `PostgresRow` instead.") - public var dataRow: PostgresMessage.DataRow { - let columns = self.data.map { - PostgresMessage.DataRow.Column(value: $0) - } - return PostgresMessage.DataRow(columns: columns) - } - - @available(*, deprecated, message: """ - This call is O(n) where n is the number of cells in the row. For random access to cells - in a row create a PostgresRandomAccessRow from the row first and use its subscript - methods. (see `makeRandomAccess()`) - """) - public func column(_ column: String) -> PostgresData? { - guard let index = self.lookupTable[column] else { - return nil - } - - return PostgresData( - type: self.columns[index].dataType, - typeModifier: self.columns[index].dataTypeModifier, - formatCode: .binary, - value: self.data[column: index] - ) - } -} - -extension PostgresRow: CustomStringConvertible { - public var description: String { - var row: [String: PostgresData] = [:] - for cell in self { - row[cell.columnName] = PostgresData( - type: cell.dataType, - typeModifier: 0, - formatCode: cell.format, - value: cell.bytes - ) - } - return row.description - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresConnection+Configuration+Deprecated.swift b/Sources/PostgresNIO/Deprecated/PostgresConnection+Configuration+Deprecated.swift deleted file mode 100644 index 9619c182..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresConnection+Configuration+Deprecated.swift +++ /dev/null @@ -1,95 +0,0 @@ -import NIOCore - -extension PostgresConnection.Configuration { - /// Legacy connection parameters structure. Replaced by ``PostgresConnection/Configuration/host`` etc. - @available(*, deprecated, message: "Use `Configuration.host` etc. instead.") - public struct Connection { - /// See ``PostgresConnection/Configuration/host``. - public var host: String - - /// See ``PostgresConnection/Configuration/port``. - public var port: Int - - /// See ``PostgresConnection/Configuration/Options-swift.struct/requireBackendKeyData``. - public var requireBackendKeyData: Bool = true - - /// See ``PostgresConnection/Configuration/Options-swift.struct/connectTimeout``. - public var connectTimeout: TimeAmount = .seconds(10) - - /// Create a configuration for connecting to a server. - /// - /// - Parameters: - /// - host: The hostname to connect to. - /// - port: The TCP port to connect to (defaults to 5432). - public init(host: String, port: Int = 5432) { - self.host = host - self.port = port - } - } - - /// Legacy authentication parameters structure. Replaced by ``PostgresConnection/Configuration/username`` etc. - @available(*, deprecated, message: "Use `Configuration.username` etc. instead.") - public struct Authentication { - /// See ``PostgresConnection/Configuration/username``. - public var username: String - - /// See ``PostgresConnection/Configuration/password``. - public var password: String? - - /// See ``PostgresConnection/Configuration/database``. - public var database: String? - - public init(username: String, database: String?, password: String?) { - self.username = username - self.database = database - self.password = password - } - } - - /// Accessor for legacy connection parameters. Replaced by ``PostgresConnection/Configuration/host`` etc. - @available(*, deprecated, message: "Use `Configuration.host` etc. instead.") - public var connection: Connection { - get { - var conn: Connection - switch self.endpointInfo { - case .connectTCP(let host, let port): - conn = .init(host: host, port: port) - case .bindUnixDomainSocket(_), .configureChannel(_): - conn = .init(host: "!invalid!", port: 0) // best we can do, really - } - conn.requireBackendKeyData = self.options.requireBackendKeyData - conn.connectTimeout = self.options.connectTimeout - return conn - } - set { - self.endpointInfo = .connectTCP(host: newValue.host, port: newValue.port) - self.options.connectTimeout = newValue.connectTimeout - self.options.requireBackendKeyData = newValue.requireBackendKeyData - } - } - - @available(*, deprecated, message: "Use `Configuration.username` etc. instead.") - public var authentication: Authentication { - get { - .init(username: self.username, database: self.database, password: self.password) - } - set { - self.username = newValue.username - self.password = newValue.password - self.database = newValue.database - } - } - - /// Legacy initializer. - /// Replaced by ``PostgresConnection/Configuration/init(host:port:username:password:database:tls:)`` etc. - @available(*, deprecated, message: "Use `init(host:port:username:password:database:tls:)` instead.") - public init(connection: Connection, authentication: Authentication, tls: TLS) { - self.init( - host: connection.host, port: connection.port, - username: authentication.username, password: authentication.password, database: authentication.database, - tls: tls - ) - self.options.connectTimeout = connection.connectTimeout - self.options.requireBackendKeyData = connection.requireBackendKeyData - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresData+UInt.swift b/Sources/PostgresNIO/Deprecated/PostgresData+UInt.swift deleted file mode 100644 index ab3e493f..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresData+UInt.swift +++ /dev/null @@ -1,162 +0,0 @@ -private func warn( - _ old: Any.Type, mustBeConvertedTo new: Any.Type, - file: StaticString = #file, line: UInt = #line -) { - assertionFailure(""" - Integer conversion unsafe. - Postgres does not support storing \(old) natively. - - To bypass this assertion, compile in release mode. - - swift build -c release - - Unsigned integers were previously allowed by PostgresNIO - but may cause overflow. To avoid overflow errors, update - your code to use \(new) instead. - - See https://github.com/vapor/postgres-nio/pull/120 - - """, file: file, line: line) -} - -extension PostgresData { - @available(*, deprecated, renamed: "init(int:)") - public init(uint value: UInt) { - warn(UInt.self, mustBeConvertedTo: Int.self) - self.init(int: .init(bitPattern: value)) - } - - @available(*, deprecated, renamed: "init(uint8:)") - public init(int8 value: Int8) { - warn(Int8.self, mustBeConvertedTo: UInt8.self) - self.init(uint8: .init(bitPattern: value)) - } - - @available(*, deprecated, renamed: "init(int16:)") - public init(uint16 value: UInt16) { - warn(UInt16.self, mustBeConvertedTo: Int16.self) - self.init(int16: .init(bitPattern: value)) - } - - @available(*, deprecated, renamed: "init(int32:)") - public init(uint32 value: UInt32) { - warn(UInt32.self, mustBeConvertedTo: Int32.self) - self.init(int32: .init(bitPattern: value)) - } - - @available(*, deprecated, renamed: "init(int64:)") - public init(uint64 value: UInt64) { - warn(UInt64.self, mustBeConvertedTo: Int64.self) - self.init(int64: .init(bitPattern: value)) - } - - @available(*, deprecated, renamed: "int") - public var uint: UInt? { - warn(UInt.self, mustBeConvertedTo: Int.self) - return self.int.flatMap { .init(bitPattern: $0) } - } - - @available(*, deprecated, renamed: "uint8") - public var int8: Int8? { - warn(Int8.self, mustBeConvertedTo: UInt8.self) - return self.uint8.flatMap { .init(bitPattern: $0) } - } - - @available(*, deprecated, renamed: "int16") - public var uint16: UInt16? { - warn(UInt16.self, mustBeConvertedTo: Int16.self) - return self.int16.flatMap { .init(bitPattern: $0) } - } - - @available(*, deprecated, renamed: "int32") - public var uint32: UInt32? { - warn(UInt32.self, mustBeConvertedTo: Int32.self) - return self.int32.flatMap { .init(bitPattern: $0) } - } - - @available(*, deprecated, renamed: "int64") - public var uint64: UInt64? { - warn(UInt64.self, mustBeConvertedTo: Int64.self) - return self.int64.flatMap { .init(bitPattern: $0) } - } -} - -@available(*, deprecated, message: "Use 'Int' instead.") -extension UInt: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { .int8 } - - public init?(postgresData: PostgresData) { - guard let uint = postgresData.uint else { - return nil - } - self = uint - } - - public var postgresData: PostgresData? { - .init(uint: self) - } -} - -@available(*, deprecated, message: "Use 'UInt8' instead.") -extension Int8: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { .char } - - public init?(postgresData: PostgresData) { - guard let int8 = postgresData.int8 else { - return nil - } - self = int8 - } - - public var postgresData: PostgresData? { - .init(int8: self) - } -} - -@available(*, deprecated, message: "Use 'Int16' instead.") -extension UInt16: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { .int2 } - - public init?(postgresData: PostgresData) { - guard let uint16 = postgresData.uint16 else { - return nil - } - self = uint16 - } - - public var postgresData: PostgresData? { - .init(uint16: self) - } -} - -@available(*, deprecated, message: "Use 'Int32' instead.") -extension UInt32: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { .int4 } - - public init?(postgresData: PostgresData) { - guard let uint32 = postgresData.uint32 else { - return nil - } - self = uint32 - } - - public var postgresData: PostgresData? { - .init(uint32: self) - } -} - -@available(*, deprecated, message: "Use 'Int64' instead.") -extension UInt64: PostgresDataConvertible { - public static var postgresDataType: PostgresDataType { .int8 } - - public init?(postgresData: PostgresData) { - guard let uint64 = postgresData.uint64 else { - return nil - } - self = uint64 - } - - public var postgresData: PostgresData? { - .init(uint64: self) - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+Authentication.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+Authentication.swift deleted file mode 100644 index da7c25d5..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+Authentication.swift +++ /dev/null @@ -1,119 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Authentication request returned by the server. - @available(*, deprecated, message: "Will be removed from public API") - public enum Authentication: PostgresMessageType { - public static var identifier: PostgresMessage.Identifier { - return .authentication - } - - /// Parses an instance of this message type from a byte buffer. - public static func parse(from buffer: inout ByteBuffer) throws -> Authentication { - guard let type = buffer.readInteger(as: Int32.self) else { - throw PostgresError.protocol("Could not read authentication message type") - } - switch type { - case 0: return .ok - case 3: return .plaintext - case 5: - guard let salt = buffer.readBytes(length: 4) else { - throw PostgresError.protocol("Could not parse MD5 salt from authentication message") - } - return .md5(salt) - case 10: - var mechanisms: [String] = [] - while buffer.readableBytes > 0 { - guard let nextString = buffer.readNullTerminatedString() else { - throw PostgresError.protocol("Could not parse SASL mechanisms from authentication message") - } - if nextString.isEmpty { - break - } - mechanisms.append(nextString) - } - guard buffer.readableBytes == 0 else { - throw PostgresError.protocol("Trailing data at end of SASL mechanisms authentication message") - } - return .saslMechanisms(mechanisms) - case 11: - guard let challengeData = buffer.readBytes(length: buffer.readableBytes) else { - throw PostgresError.protocol("Could not parse SASL challenge from authentication message") - } - return .saslContinue(challengeData) - case 12: - guard let finalData = buffer.readBytes(length: buffer.readableBytes) else { - throw PostgresError.protocol("Could not parse SASL final data from authentication message") - } - return .saslFinal(finalData) - - case 2, 7...9: - throw PostgresError.protocol("Support for KRBv5, GSSAPI, and SSPI authentication are not implemented") - case 6: - throw PostgresError.protocol("Support for SCM credential authentication is obsolete") - - default: - throw PostgresError.protocol("Unknown authentication request type: \(type)") - } - } - - public func serialize(into buffer: inout ByteBuffer) throws { - switch self { - case .ok: - buffer.writeInteger(0, as: Int32.self) - case .plaintext: - buffer.writeInteger(3, as: Int32.self) - case .md5(let salt): - buffer.writeInteger(5, as: Int32.self) - buffer.writeBytes(salt) - case .saslMechanisms(let mechanisms): - buffer.writeInteger(10, as: Int32.self) - mechanisms.forEach { - buffer.writeNullTerminatedString($0) - } - case .saslContinue(let challenge): - buffer.writeInteger(11, as: Int32.self) - buffer.writeBytes(challenge) - case .saslFinal(let data): - buffer.writeInteger(12, as: Int32.self) - buffer.writeBytes(data) - } - } - - /// AuthenticationOk - /// Specifies that the authentication was successful. - case ok - - /// AuthenticationCleartextPassword - /// Specifies that a clear-text password is required. - case plaintext - - /// AuthenticationMD5Password - /// Specifies that an MD5-encrypted password is required. - case md5([UInt8]) - - /// AuthenticationSASL - /// Specifies the start of SASL mechanism negotiation. - case saslMechanisms([String]) - - /// AuthenticationSASLContinue - /// Specifies SASL mechanism-specific challenge data. - case saslContinue([UInt8]) - - /// AuthenticationSASLFinal - /// Specifies mechanism-specific post-authentication client data. - case saslFinal([UInt8]) - - /// See `CustomStringConvertible`. - public var description: String { - switch self { - case .ok: return "Ok" - case .plaintext: return "CleartextPassword" - case .md5(let salt): return "MD5Password(salt: 0x\(salt.hexdigest()))" - case .saslMechanisms(let mech): return "SASLMechanisms(\(mech))" - case .saslContinue(let data): return "SASLChallenge(\(data))" - case .saslFinal(let data): return "SASLFinal(\(data))" - } - } - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+Bind.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+Bind.swift deleted file mode 100644 index 5ff4bbf0..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+Bind.swift +++ /dev/null @@ -1,61 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Identifies the message as a Bind command. - @available(*, deprecated, message: "Will be removed from public API") - public struct Bind: PostgresMessageType { - public static var identifier: PostgresMessage.Identifier { - return .bind - } - - public var description: String { - return "Bind(\(self.parameters.count))" - } - - public struct Parameter { - /// The value of the parameter, in the format indicated by the associated format code. n is the above length. - var value: ByteBuffer? - } - - /// The name of the destination portal (an empty string selects the unnamed portal). - public var portalName: String - - /// The name of the source prepared statement (an empty string selects the unnamed prepared statement). - public var statementName: String - - /// The number of parameter format codes that follow (denoted C below). - /// This can be zero to indicate that there are no parameters or that the parameters all use the default format (text); - /// or one, in which case the specified format code is applied to all parameters; or it can equal the actual number of parameters. - /// The parameter format codes. Each must presently be zero (text) or one (binary). - public var parameterFormatCodes: [PostgresFormat] - - /// The number of parameter values that follow (possibly zero). This must match the number of parameters needed by the query. - public var parameters: [Parameter] - - /// The number of result-column format codes that follow (denoted R below). - /// This can be zero to indicate that there are no result columns or that the result columns should all use the default format (text); - /// or one, in which case the specified format code is applied to all result columns (if any); - /// or it can equal the actual number of result columns of the query. - public var resultFormatCodes: [PostgresFormat] - - /// Serializes this message into a byte buffer. - public func serialize(into buffer: inout ByteBuffer) { - buffer.writeNullTerminatedString(self.portalName) - buffer.writeNullTerminatedString(self.statementName) - - buffer.write(array: self.parameterFormatCodes) - buffer.write(array: self.parameters) { - if var data = $1.value { - // The length of the parameter value, in bytes (this count does not include itself). Can be zero. - $0.writeInteger(numericCast(data.readableBytes), as: Int32.self) - // The value of the parameter, in the format indicated by the associated format code. n is the above length. - $0.writeBuffer(&data) - } else { - // As a special case, -1 indicates a NULL parameter value. No value bytes follow in the NULL case. - $0.writeInteger(-1, as: Int32.self) - } - } - buffer.write(array: self.resultFormatCodes) - } - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+Close.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+Close.swift deleted file mode 100644 index 9bcc8aa1..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+Close.swift +++ /dev/null @@ -1,40 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Identifies the message as a Close Command - @available(*, deprecated, message: "Will be removed from public API") - public struct Close: PostgresMessageType { - public static var identifier: PostgresMessage.Identifier { - return .close - } - - /// Close Target. Determines if the Close command should close a prepared statement - /// or portal. - public enum Target: Int8 { - case preparedStatement = 0x53 // 'S' - prepared statement - case portal = 0x50 // 'P' - portal - } - - /// Determines if the `name` identifes a portal or a prepared statement - public var target: Target - - /// The name of the prepared statement or portal to describe - /// (an empty string selects the unnamed prepared statement or portal). - public var name: String - - - /// See `CustomStringConvertible`. - public var description: String { - switch target { - case .preparedStatement: return "Statement(\(name))" - case .portal: return "Portal(\(name))" - } - } - - /// Serializes this message into a byte buffer. - public func serialize(into buffer: inout ByteBuffer) throws { - buffer.writeInteger(target.rawValue) - buffer.writeNullTerminatedString(name) - } - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+CommandComplete.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+CommandComplete.swift deleted file mode 100644 index c9370402..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+CommandComplete.swift +++ /dev/null @@ -1,18 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Identifies the message as a Close command. - @available(*, deprecated, message: "Will be removed from public API") - public struct CommandComplete: PostgresMessageType { - /// Parses an instance of this message type from a byte buffer. - public static func parse(from buffer: inout ByteBuffer) throws -> CommandComplete { - guard let string = buffer.readNullTerminatedString() else { - throw PostgresError.protocol("Could not parse close response message") - } - return .init(tag: string) - } - - /// The command tag. This is usually a single word that identifies which SQL command was completed. - public var tag: String - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+Describe.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+Describe.swift deleted file mode 100644 index 787355db..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+Describe.swift +++ /dev/null @@ -1,38 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Identifies the message as a Describe command. - @available(*, deprecated, message: "Will be removed from public API") - public struct Describe: PostgresMessageType { - public static var identifier: PostgresMessage.Identifier { - return .describe - } - - /// Command type. - public enum Command: UInt8 { - case statement = 0x53 // S - case portal = 0x50 // P - } - - /// 'S' to describe a prepared statement; or 'P' to describe a portal. - public let command: Command - - /// The name of the prepared statement or portal to describe - /// (an empty string selects the unnamed prepared statement or portal). - public var name: String - - /// See `CustomStringConvertible`. - public var description: String { - switch command { - case .statement: return "Statement(" + name + ")" - case .portal: return "Portal(" + name + ")" - } - } - - /// Serializes this message into a byte buffer. - public func serialize(into buffer: inout ByteBuffer) { - buffer.writeInteger(command.rawValue) - buffer.writeNullTerminatedString(name) - } - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+Execute.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+Execute.swift deleted file mode 100644 index 39b447a4..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+Execute.swift +++ /dev/null @@ -1,28 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Identifies the message as an Execute command. - @available(*, deprecated, message: "Will be removed from public API") - public struct Execute: PostgresMessageType { - public static var identifier: PostgresMessage.Identifier { - return .execute - } - - public var description: String { - return "Execute()" - } - - /// The name of the destination portal (an empty string selects the unnamed portal). - public var portalName: String - - /// Maximum number of rows to return, if portal contains a query that - /// returns rows (ignored otherwise). Zero denotes “no limit”. - public var maxRows: Int32 - - /// Serializes this message into a byte buffer. - public func serialize(into buffer: inout ByteBuffer) { - buffer.writeNullTerminatedString(portalName) - buffer.writeInteger(self.maxRows) - } - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+ParameterDescription.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+ParameterDescription.swift deleted file mode 100644 index 89e67682..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+ParameterDescription.swift +++ /dev/null @@ -1,23 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Identifies the message as a parameter description. - @available(*, deprecated, message: "Will be removed from public API") - public struct ParameterDescription: PostgresMessageType { - /// Parses an instance of this message type from a byte buffer. - public static func parse(from buffer: inout ByteBuffer) throws -> ParameterDescription { - guard let dataTypes = try buffer.read(array: PostgresDataType.self, { buffer in - guard let dataType = buffer.readInteger(as: PostgresDataType.self) else { - throw PostgresError.protocol("Could not parse data type integer in parameter description message.") - } - return dataType - }) else { - throw PostgresError.protocol("Could not parse data types in parameter description message.") - } - return .init(dataTypes: dataTypes) - } - - /// Specifies the object ID of the parameter data type. - public var dataTypes: [PostgresDataType] - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+ParameterStatus.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+ParameterStatus.swift deleted file mode 100644 index 5ad6f95e..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+ParameterStatus.swift +++ /dev/null @@ -1,28 +0,0 @@ -import NIOCore - -extension PostgresMessage { - @available(*, deprecated, message: "Will be removed from public API") - public struct ParameterStatus: PostgresMessageType, CustomStringConvertible { - /// Parses an instance of this message type from a byte buffer. - public static func parse(from buffer: inout ByteBuffer) throws -> ParameterStatus { - guard let parameter = buffer.readNullTerminatedString() else { - throw PostgresError.protocol("Could not read parameter from parameter status message") - } - guard let value = buffer.readNullTerminatedString() else { - throw PostgresError.protocol("Could not read value from parameter status message") - } - return .init(parameter: parameter, value: value) - } - - /// The name of the run-time parameter being reported. - public var parameter: String - - /// The current value of the parameter. - public var value: String - - /// See `CustomStringConvertible`. - public var description: String { - return "\(parameter): \(value)" - } - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+Parse.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+Parse.swift deleted file mode 100644 index 8fb5a1ff..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+Parse.swift +++ /dev/null @@ -1,31 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Identifies the message as a Parse command. - @available(*, deprecated, message: "Will be removed from public API") - public struct Parse: PostgresMessageType { - public static var identifier: PostgresMessage.Identifier { - return .parse - } - - /// The name of the destination prepared statement (an empty string selects the unnamed prepared statement). - public var statementName: String - - /// The query string to be parsed. - public var query: String - - /// The number of parameter data types specified (can be zero). - /// Note that this is not an indication of the number of parameters that might appear in the - /// query string, only the number that the frontend wants to prespecify types for. - /// Specifies the object ID of the parameter data type. Placing a zero here is equivalent to leaving the type unspecified. - public var parameterTypes: [PostgresDataType] - - /// Serializes this message into a byte buffer. - public func serialize(into buffer: inout ByteBuffer) { - buffer.writeString(statementName + "\0") - buffer.writeString(query + "\0") - buffer.writeInteger(numericCast(self.parameterTypes.count), as: Int16.self) - self.parameterTypes.forEach { buffer.writeInteger($0.rawValue) } - } - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+Password.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+Password.swift deleted file mode 100644 index cafe9cda..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+Password.swift +++ /dev/null @@ -1,29 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Identifies the message as a password response. Note that this is also used for - /// GSSAPI and SSPI response messages (which is really a design error, since the contained - /// data is not a null-terminated string in that case, but can be arbitrary binary data). - @available(*, deprecated, message: "Will be removed from public API") - public struct Password: PostgresMessageType { - public static var identifier: PostgresMessage.Identifier { - return .passwordMessage - } - - public init(string: String) { - self.string = string - } - - /// The password (encrypted, if requested). - public var string: String - - public var description: String { - return "Password(\(string))" - } - - /// Serializes this message into a byte buffer. - public func serialize(into buffer: inout ByteBuffer) { - buffer.writeString(self.string + "\0") - } - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+ReadyForQuery.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+ReadyForQuery.swift deleted file mode 100644 index 5afc0910..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+ReadyForQuery.swift +++ /dev/null @@ -1,27 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Identifies the message type. ReadyForQuery is sent whenever the backend is ready for a new query cycle. - @available(*, deprecated, message: "Will be removed from public API") - public struct ReadyForQuery: CustomStringConvertible { - /// Parses an instance of this message type from a byte buffer. - public static func parse(from buffer: inout ByteBuffer) throws -> ReadyForQuery { - guard let status = buffer.readInteger(as: UInt8.self) else { - throw PostgresError.protocol("Could not read transaction status from ready for query message") - } - return .init(transactionStatus: status) - } - - /// Current backend transaction status indicator. - /// Possible values are 'I' if idle (not in a transaction block); - /// 'T' if in a transaction block; or 'E' if in a failed transaction block - /// (queries will be rejected until block is ended). - public var transactionStatus: UInt8 - - /// See `CustomStringConvertible`. - public var description: String { - let char = String(bytes: [transactionStatus], encoding: .ascii) ?? "n/a" - return "transactionStatus: \(char)" - } - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+SASLResponse.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+SASLResponse.swift deleted file mode 100644 index dc3b1772..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+SASLResponse.swift +++ /dev/null @@ -1,78 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// SASL ongoing challenge response message sent by the client. - @available(*, deprecated, message: "Will be removed from public API") - public struct SASLResponse: PostgresMessageType { - public static var identifier: PostgresMessage.Identifier { - return .saslResponse - } - - public let responseData: [UInt8] - - public static func parse(from buffer: inout ByteBuffer) throws -> SASLResponse { - guard let data = buffer.readBytes(length: buffer.readableBytes) else { - throw PostgresError.protocol("Could not parse SASL response from response message") - } - - return SASLResponse(responseData: data) - } - - public func serialize(into buffer: inout ByteBuffer) throws { - buffer.writeBytes(responseData) - } - - public var description: String { - return "SASLResponse(\(responseData))" - } - } -} - -extension PostgresMessage { - /// SASL initial challenge response message sent by the client. - @available(*, deprecated, message: "Will be removed from public API") - public struct SASLInitialResponse { - public let mechanism: String - public let initialData: [UInt8] - - public func serialize(into buffer: inout ByteBuffer) throws { - buffer.writeNullTerminatedString(self.mechanism) - if initialData.count > 0 { - buffer.writeInteger(Int32(initialData.count), as: Int32.self) // write(array:) writes Int16, which is incorrect here - buffer.writeBytes(initialData) - } else { - buffer.writeInteger(-1, as: Int32.self) - } - } - - public var description: String { - return "SASLInitialResponse(\(mechanism), data: \(initialData))" - } - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresMessageType` since it is deprecated.") -extension PostgresMessage.SASLInitialResponse: PostgresMessageType { - public static var identifier: PostgresMessage.Identifier { - return .saslInitialResponse - } - - public static func parse(from buffer: inout ByteBuffer) throws -> Self { - guard let mechanism = buffer.readNullTerminatedString() else { - throw PostgresError.protocol("Could not parse SASL mechanism from initial response message") - } - guard let dataLength = buffer.readInteger(as: Int32.self) else { - throw PostgresError.protocol("Could not parse SASL initial data length from initial response message") - } - - var actualData: [UInt8] = [] - - if dataLength != -1 { - guard let data = buffer.readBytes(length: Int(dataLength)) else { - throw PostgresError.protocol("Could not parse SASL initial data from initial response message") - } - actualData = data - } - return .init(mechanism: mechanism, initialData: actualData) - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+SSLRequest.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+SSLRequest.swift deleted file mode 100644 index ee504932..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+SSLRequest.swift +++ /dev/null @@ -1,27 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// A message asking the PostgreSQL server if SSL is supported - /// For more info, see https://www.postgresql.org/docs/10/static/protocol-flow.html#id-1.10.5.7.11 - @available(*, deprecated, message: "Will be removed from public API") - public struct SSLRequest: PostgresMessageType { - /// The SSL request code. The value is chosen to contain 1234 in the most significant 16 bits, - /// and 5679 in the least significant 16 bits. - public let code: Int32 - - /// See `CustomStringConvertible`. - public var description: String { - return "SSLRequest" - } - - /// Creates a new `SSLRequest`. - public init() { - self.code = 80877103 - } - - /// Serializes this message into a byte buffer. - public func serialize(into buffer: inout ByteBuffer) { - buffer.writeInteger(self.code) - } - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+SimpleQuery.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+SimpleQuery.swift deleted file mode 100644 index a0a6cfcf..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+SimpleQuery.swift +++ /dev/null @@ -1,19 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Identifies the message as a simple query. - @available(*, deprecated, message: "Will be removed from public API") - public struct SimpleQuery: PostgresMessageType { - public static var identifier: PostgresMessage.Identifier { - return .query - } - - /// The query string itself. - public var string: String - - /// Serializes this message into a byte buffer. - public func serialize(into buffer: inout ByteBuffer) { - buffer.writeString(self.string + "\0") - } - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+Startup.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+Startup.swift deleted file mode 100644 index e9762439..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+Startup.swift +++ /dev/null @@ -1,48 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// First message sent from the frontend during startup. - @available(*, deprecated, message: "Will be removed from public API") - public struct Startup: PostgresMessageType { - public static var identifier: PostgresMessage.Identifier { - return .none - } - - public var description: String { - return "Startup()" - } - - /// Creates a `Startup` with "3.0" as the protocol version. - public static func versionThree(parameters: [String: String]) -> Startup { - return .init(protocolVersion: 0x00_03_00_00, parameters: parameters) - } - - /// The protocol version number. The most significant 16 bits are the major - /// version number (3 for the protocol described here). The least significant - /// 16 bits are the minor version number (0 for the protocol described here). - public var protocolVersion: Int32 - - /// The protocol version number is followed by one or more pairs of parameter - /// name and value strings. A zero byte is required as a terminator after - /// the last name/value pair. Parameters can appear in any order. user is required, - /// others are optional. Each parameter is specified as: - public var parameters: [String: String] - - /// Creates a new `PostgreSQLStartupMessage`. - public init(protocolVersion: Int32, parameters: [String: String]) { - self.protocolVersion = protocolVersion - self.parameters = parameters - } - - /// Serializes this message into a byte buffer. - public func serialize(into buffer: inout ByteBuffer) { - buffer.writeInteger(self.protocolVersion) - for (key, val) in parameters { - buffer.writeString(key + "\0") - buffer.writeString(val + "\0") - } - // terminator - buffer.writeString("\0") - } - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+Sync.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+Sync.swift deleted file mode 100644 index 0560ef7a..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+Sync.swift +++ /dev/null @@ -1,19 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Identifies the message as a Bind command. - @available(*, deprecated, message: "Will be removed from public API") - public struct Sync: PostgresMessageType { - public static var identifier: PostgresMessage.Identifier { - return .sync - } - - public var description: String { - return "Sync" - } - - public func serialize(into buffer: inout ByteBuffer) { - - } - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessage+Terminate.swift b/Sources/PostgresNIO/Deprecated/PostgresMessage+Terminate.swift deleted file mode 100644 index afeae5bf..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessage+Terminate.swift +++ /dev/null @@ -1,12 +0,0 @@ -import NIOCore - -extension PostgresMessage { - @available(*, deprecated, message: "Will be removed from public API") - public struct Terminate: PostgresMessageType { - public static var identifier: PostgresMessage.Identifier { - .terminate - } - - public func serialize(into buffer: inout ByteBuffer) { } - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessageDecoder.swift b/Sources/PostgresNIO/Deprecated/PostgresMessageDecoder.swift deleted file mode 100644 index e092c234..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessageDecoder.swift +++ /dev/null @@ -1,66 +0,0 @@ -import NIOCore -import Logging - -@available(*, deprecated, message: "Will be removed from public API") -public final class PostgresMessageDecoder: ByteToMessageDecoder { - /// See `ByteToMessageDecoder`. - public typealias InboundOut = PostgresMessage - - /// See `ByteToMessageDecoder`. - public var cumulationBuffer: ByteBuffer? - - /// If `true`, the server has asked for authentication. - public var hasSeenFirstMessage: Bool - - /// Logger to send debug messages to. - let logger: Logger? - - /// Creates a new `PostgresMessageDecoder`. - public init(logger: Logger? = nil) { - self.hasSeenFirstMessage = false - self.logger = logger - } - - /// See `ByteToMessageDecoder`. - public func decode(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState { - var peekBuffer = buffer - - // peek at the message identifier - // the message identifier is always the first byte of a message - guard let identifier = peekBuffer.readInteger(as: UInt8.self).map(PostgresMessage.Identifier.init) else { - return .needMoreData - } - - let message: PostgresMessage - - // special ssl case, no body - if !self.hasSeenFirstMessage && (identifier == .sslSupported || identifier == .sslUnsupported) { - message = PostgresMessage(identifier: identifier, data: context.channel.allocator.buffer(capacity: 0)) - } else { - // peek at the message size - // the message size is always a 4 byte integer appearing immediately after the message identifier - guard let messageSize = peekBuffer.readInteger(as: Int32.self).flatMap(Int.init) else { - return .needMoreData - } - - // ensure message is large enough (skipping message type) or reject - guard let data = peekBuffer.readSlice(length: messageSize - 4) else { - return .needMoreData - } - - message = PostgresMessage(identifier: identifier, data: data) - } - self.hasSeenFirstMessage = true - - // there is sufficient data, use this buffer - buffer = peekBuffer - self.logger?.trace("Decoded: PostgresMessage (\(message.identifier))") - context.fireChannelRead(wrapInboundOut(message)) - return .continue - } - - public func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState { - // ignore - return .needMoreData - } -} diff --git a/Sources/PostgresNIO/Deprecated/PostgresMessageEncoder.swift b/Sources/PostgresNIO/Deprecated/PostgresMessageEncoder.swift deleted file mode 100644 index 8dd4c38d..00000000 --- a/Sources/PostgresNIO/Deprecated/PostgresMessageEncoder.swift +++ /dev/null @@ -1,42 +0,0 @@ -import NIOCore -import Logging - -@available(*, deprecated, message: "Will be removed from public API") -public final class PostgresMessageEncoder: MessageToByteEncoder { - /// See `MessageToByteEncoder`. - public typealias OutboundIn = PostgresMessage - - /// Logger to send debug messages to. - let logger: Logger? - - /// Creates a new `PostgresMessageEncoder`. - public init(logger: Logger? = nil) { - self.logger = logger - } - - /// See `MessageToByteEncoder`. - public func encode(data message: PostgresMessage, out: inout ByteBuffer) throws { - // serialize identifier - var message = message - switch message.identifier { - case .none: break - default: - out.write(identifier: message.identifier) - } - - // leave room for identifier and size - let messageSizeIndex = out.writerIndex - out.moveWriterIndex(forwardBy: 4) - - // serialize the message data - out.writeBuffer(&message.data) - - // set message size - out.setInteger(Int32(out.writerIndex - messageSizeIndex), at: messageSizeIndex) - self.logger?.trace("Encoded: PostgresMessage (\(message.identifier))") - } -} - -protocol ByteBufferSerializable { - func serialize(into buffer: inout ByteBuffer) -} diff --git a/Sources/PostgresNIO/Docs.docc/index.md b/Sources/PostgresNIO/Docs.docc/index.md index 6355a7a4..663c6c5c 100644 --- a/Sources/PostgresNIO/Docs.docc/index.md +++ b/Sources/PostgresNIO/Docs.docc/index.md @@ -50,9 +50,5 @@ configure ``PostgresConnection`` to use `Network.framework` as the underlying tr - ``PostgresDecodingError`` - ``PSQLError`` -### Deprecations - -- <doc:deprecated> - [SwiftNIO]: https://github.com/apple/swift-nio [SwiftLog]: https://github.com/apple/swift-log diff --git a/Sources/PostgresNIO/Message/PostgresMessage+0.swift b/Sources/PostgresNIO/Message/PostgresMessage+0.swift deleted file mode 100644 index 386ffd34..00000000 --- a/Sources/PostgresNIO/Message/PostgresMessage+0.swift +++ /dev/null @@ -1,23 +0,0 @@ -import NIOCore - -/// A frontend or backend Postgres message. -public struct PostgresMessage: Equatable { - @available(*, deprecated, message: "Will be removed from public API.") - public var identifier: Identifier - public var data: ByteBuffer - - @available(*, deprecated, message: "Will be removed from public API.") - public init<Data>(identifier: Identifier, bytes: Data) - where Data: Sequence, Data.Element == UInt8 - { - var buffer = ByteBufferAllocator().buffer(capacity: 0) - buffer.writeBytes(bytes) - self.init(identifier: identifier, data: buffer) - } - - @available(*, deprecated, message: "Will be removed from public API.") - public init(identifier: Identifier, data: ByteBuffer) { - self.identifier = identifier - self.data = data - } -} diff --git a/Sources/PostgresNIO/Message/PostgresMessage+BackendKeyData.swift b/Sources/PostgresNIO/Message/PostgresMessage+BackendKeyData.swift deleted file mode 100644 index 63a6af7d..00000000 --- a/Sources/PostgresNIO/Message/PostgresMessage+BackendKeyData.swift +++ /dev/null @@ -1,31 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Identifies the message as cancellation key data. - /// The frontend must save these values if it wishes to be able to issue CancelRequest messages later. - public struct BackendKeyData { - /// The process ID of this backend. - public var processID: Int32 - - /// The secret key of this backend. - public var secretKey: Int32 - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresMessageType` since it is deprecated.") -extension PostgresMessage.BackendKeyData: PostgresMessageType { - public static var identifier: PostgresMessage.Identifier { - .backendKeyData - } - - /// Parses an instance of this message type from a byte buffer. - public static func parse(from buffer: inout ByteBuffer) throws -> Self { - guard let processID = buffer.readInteger(as: Int32.self) else { - throw PostgresError.protocol("Could not parse process id from backend key data") - } - guard let secretKey = buffer.readInteger(as: Int32.self) else { - throw PostgresError.protocol("Could not parse secret key from backend key data") - } - return .init(processID: processID, secretKey: secretKey) - } -} diff --git a/Sources/PostgresNIO/Message/PostgresMessage+DataRow.swift b/Sources/PostgresNIO/Message/PostgresMessage+DataRow.swift deleted file mode 100644 index 655bfb1e..00000000 --- a/Sources/PostgresNIO/Message/PostgresMessage+DataRow.swift +++ /dev/null @@ -1,54 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Identifies the message as a data row. - public struct DataRow { - public struct Column: CustomStringConvertible { - /// The length of the column value, in bytes (this count does not include itself). - /// Can be zero. As a special case, -1 indicates a NULL column value. No value bytes follow in the NULL case. - - /// The value of the column, in the format indicated by the associated format code. n is the above length. - public var value: ByteBuffer? - - /// See `CustomStringConvertible`. - public var description: String { - if let value = value { - return "0x" + value.readableBytesView.hexdigest() - } else { - return "<null>" - } - } - } - - /// The data row's columns - public var columns: [Column] - - /// See `CustomStringConvertible`. - public var description: String { - return "Columns(" + columns.map { $0.description }.joined(separator: ", ") + ")" - } - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresMessageType` since it is deprecated.") -extension PostgresMessage.DataRow: PostgresMessageType { - public static var identifier: PostgresMessage.Identifier { - return .dataRow - } - - /// Parses an instance of this message type from a byte buffer. - public static func parse(from buffer: inout ByteBuffer) throws -> Self { - guard let columns = buffer.read(array: Column.self, { buffer in - if var slice = buffer.readNullableBytes() { - var copy = ByteBufferAllocator().buffer(capacity: slice.readableBytes) - copy.writeBuffer(&slice) - return .init(value: copy) - } else { - return .init(value: nil) - } - }) else { - throw PostgresError.protocol("Could not parse data row columns") - } - return .init(columns: columns) - } -} diff --git a/Sources/PostgresNIO/Message/PostgresMessage+Error.swift b/Sources/PostgresNIO/Message/PostgresMessage+Error.swift deleted file mode 100644 index 45cda21f..00000000 --- a/Sources/PostgresNIO/Message/PostgresMessage+Error.swift +++ /dev/null @@ -1,113 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// First message sent from the frontend during startup. - public struct Error: CustomStringConvertible, Sendable { - public enum Field: UInt8, Hashable, Sendable { - /// Severity: the field contents are ERROR, FATAL, or PANIC (in an error message), - /// or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a - //// localized translation of one of these. Always present. - case localizedSeverity = 0x53 /// S - - /// Severity: the field contents are ERROR, FATAL, or PANIC (in an error message), - /// or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message). - /// This is identical to the S field except that the contents are never localized. - /// This is present only in messages generated by PostgreSQL versions 9.6 and later. - case severity = 0x56 /// V - - /// Code: the SQLSTATE code for the error (see Appendix A). Not localizable. Always present. - case sqlState = 0x43 /// C - - /// Message: the primary human-readable error message. This should be accurate but terse (typically one line). - /// Always present. - case message = 0x4D /// M - - /// Detail: an optional secondary error message carrying more detail about the problem. - /// Might run to multiple lines. - case detail = 0x44 /// D - - /// Hint: an optional suggestion what to do about the problem. - /// This is intended to differ from Detail in that it offers advice (potentially inappropriate) - /// rather than hard facts. Might run to multiple lines. - case hint = 0x48 /// H - - /// Position: the field value is a decimal ASCII integer, indicating an error cursor - /// position as an index into the original query string. The first character has index 1, - /// and positions are measured in characters not bytes. - case position = 0x50 /// P - - /// Internal position: this is defined the same as the P field, but it is used when the - /// cursor position refers to an internally generated command rather than the one submitted by the client. - /// The q field will always appear when this field appears. - case internalPosition = 0x70 /// p - - /// Internal query: the text of a failed internally-generated command. - /// This could be, for example, a SQL query issued by a PL/pgSQL function. - case internalQuery = 0x71 /// q - - /// Where: an indication of the context in which the error occurred. - /// Presently this includes a call stack traceback of active procedural language functions and - /// internally-generated queries. The trace is one entry per line, most recent first. - case locationContext = 0x57 /// W - - /// Schema name: if the error was associated with a specific database object, the name of - /// the schema containing that object, if any. - case schemaName = 0x73 /// s - - /// Table name: if the error was associated with a specific table, the name of the table. - /// (Refer to the schema name field for the name of the table's schema.) - case tableName = 0x74 /// t - - /// Column name: if the error was associated with a specific table column, the name of the column. - /// (Refer to the schema and table name fields to identify the table.) - case columnName = 0x63 /// c - - /// Data type name: if the error was associated with a specific data type, the name of the data type. - /// (Refer to the schema name field for the name of the data type's schema.) - case dataTypeName = 0x64 /// d - - /// Constraint name: if the error was associated with a specific constraint, the name of the constraint. - /// Refer to fields listed above for the associated table or domain. (For this purpose, indexes are - /// treated as constraints, even if they weren't created with constraint syntax.) - case constraintName = 0x6E /// n - - /// File: the file name of the source-code location where the error was reported. - case file = 0x46 /// F - - /// Line: the line number of the source-code location where the error was reported. - case line = 0x4C /// L - - /// Routine: the name of the source-code routine reporting the error. - case routine = 0x52 /// R - } - - /// The diagnostic messages. - public var fields: [Field: String] - - /// See `CustomStringConvertible`. - public var description: String { - let unique = self.fields[.routine] ?? self.fields[.sqlState] ?? "unknown" - let message = self.fields[.message] ?? "Unknown" - return "\(message) (\(unique))" - } - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresMessageType` since it is deprecated.") -extension PostgresMessage.Error: PostgresMessageType { - public static var identifier: PostgresMessage.Identifier { - return .error - } - - /// Parses an instance of this message type from a byte buffer. - public static func parse(from buffer: inout ByteBuffer) throws -> Self { - var fields: [Field: String] = [:] - while let field = buffer.readInteger(as: Field.self) { - guard let string = buffer.readNullTerminatedString() else { - throw PostgresError.protocol("Could not read error response string.") - } - fields[field] = string - } - return .init(fields: fields) - } -} diff --git a/Sources/PostgresNIO/Message/PostgresMessage+Identifier.swift b/Sources/PostgresNIO/Message/PostgresMessage+Identifier.swift deleted file mode 100644 index 5d111e3b..00000000 --- a/Sources/PostgresNIO/Message/PostgresMessage+Identifier.swift +++ /dev/null @@ -1,151 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Identifies an incoming or outgoing postgres message. Sent as the first byte, before the message size. - /// Values are not unique across all identifiers, meaning some messages will require keeping state to identify. - @available(*, deprecated, message: "Will be removed from public API.") - public struct Identifier: Sendable, ExpressibleByIntegerLiteral, Equatable, CustomStringConvertible { - // special - public static let none: Identifier = 0x00 - // special - public static let sslSupported: Identifier = 0x53 // 'S' - // special - public static let sslUnsupported: Identifier = 0x4E // 'N' - - /// Authentication (B) - public static let authentication: Identifier = 0x52 // 'R' - - /// BackendKeyData (B) - public static let backendKeyData: Identifier = 0x4B // 'K' - - /// Bind (F) - public static let bind: Identifier = 0x42 // 'B' - - /// BindComplete (B) - public static let bindComplete: Identifier = 0x32 // '2' - - /// Close (F) - public static let close: Identifier = 0x43 // 'C' - - /// CloseComplete (B) - public static let closeComplete: Identifier = 0x33 // '3' - - /// CommandComplete (B) - public static let commandComplete: Identifier = 0x43 // 'C' - - /// CopyData (F & B) - public static let copyData: Identifier = 0x64 // 'd' - - /// CopyDone (F & B) - public static let copyDone: Identifier = 0x63 // 'c' - - /// CopyFail (F) - public static let copyFail: Identifier = 0x66 // 'f' - - /// CopyInResponse (B) - public static let copyInResponse: Identifier = 0x47 // 'G' - - /// CopyOutResponse (B) - public static let copyOutResponse: Identifier = 0x48 // 'H' - - // CopyBothResponse (B) - public static let copyBothResponse: Identifier = 0x57 // 'W' - - /// DataRow (B) - public static let dataRow: Identifier = 0x44 // 'D' - - /// Describe (F) - public static let describe: Identifier = 0x44 // 'D' - - /// EmptyQueryResponse (B) - public static let emptyQueryResponse: Identifier = 0x49 // 'I' - - /// ErrorResponse (B) - public static let error: Identifier = 0x45 // 'E' - - /// Execute (F) - public static let execute: Identifier = 0x45 // 'E' - - /// Flush (F) - public static let flush: Identifier = 0x48 // 'H' - - /// FunctionCall (F) - public static let functionCall: Identifier = 0x46 // 'F' - - /// FunctionCallResponse (B) - public static let functionCallResponse: Identifier = 0x56 // 'V' - - /// GSSResponse (F) - public static let gssResponse: Identifier = 0x70 // 'p' - - /// NegotiateProtocolVersion (B) - public static let negotiateProtocolVersion: Identifier = 0x76 // 'v' - - /// NoData (B) - public static let noData: Identifier = 0x6E // 'n' - - /// NoticeResponse (B) - public static let notice: Identifier = 0x4E // 'N' - - /// NotificationResponse (B) - public static let notificationResponse: Identifier = 0x41 // 'A' - - /// ParameterDescription (B) - public static let parameterDescription: Identifier = 0x74 // 't' - - /// ParameterStatus (B) - public static let parameterStatus: Identifier = 0x53 // 'S' - - /// Parse (F) - public static let parse: Identifier = 0x50 // 'P' - - /// ParseComplete (B) - public static let parseComplete: Identifier = 0x31 // '1' - - /// PasswordMessage (F) - public static let passwordMessage: Identifier = 0x70 // 'p' - - /// PortalSuspended (B) - public static let portalSuspended: Identifier = 0x73 // 's' - - /// Query (F) - public static let query: Identifier = 0x51 // 'Q' - - /// ReadyForQuery (B) - public static let readyForQuery: Identifier = 0x5A // 'Z' - - /// RowDescription (B) - public static let rowDescription: Identifier = 0x54 // 'T' - - /// SASLInitialResponse (F) - public static let saslInitialResponse: Identifier = 0x70 // 'p' - - /// SASLResponse (F) - public static let saslResponse: Identifier = 0x70 // 'p' - - /// Sync (F) - public static let sync: Identifier = 0x53 // 'S' - - /// Terminate (F) - public static let terminate: Identifier = 0x58 // 'X' - - public let value: UInt8 - - /// See `CustomStringConvertible`. - public var description: String { - return String(Unicode.Scalar(self.value)) - } - - /// See `ExpressibleByIntegerLiteral`. - public init(integerLiteral value: UInt8) { - self.value = value - } - } -} - -extension ByteBuffer { - @available(*, deprecated, message: "Will be removed from public API") - mutating func write(identifier: PostgresMessage.Identifier) { - self.writeInteger(identifier.value) - } -} diff --git a/Sources/PostgresNIO/Message/PostgresMessage+NotificationResponse.swift b/Sources/PostgresNIO/Message/PostgresMessage+NotificationResponse.swift deleted file mode 100644 index 1a3b596d..00000000 --- a/Sources/PostgresNIO/Message/PostgresMessage+NotificationResponse.swift +++ /dev/null @@ -1,29 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Identifies the message as a notification response. - public struct NotificationResponse { - public var backendPID: Int32 - public var channel: String - public var payload: String - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresMessageType` since it is deprecated.") -extension PostgresMessage.NotificationResponse: PostgresMessageType { - public static let identifier = PostgresMessage.Identifier.notificationResponse - - /// Parses an instance of this message type from a byte buffer. - public static func parse(from buffer: inout ByteBuffer) throws -> Self { - guard let backendPID: Int32 = buffer.readInteger() else { - throw PostgresError.protocol("Invalid NotificationResponse message: unable to read backend PID") - } - guard let channel = buffer.readNullTerminatedString() else { - throw PostgresError.protocol("Invalid NotificationResponse message: unable to read channel") - } - guard let payload = buffer.readNullTerminatedString() else { - throw PostgresError.protocol("Invalid NotificationResponse message: unable to read payload") - } - return .init(backendPID: backendPID, channel: channel, payload: payload) - } -} diff --git a/Sources/PostgresNIO/Message/PostgresMessage+RowDescription.swift b/Sources/PostgresNIO/Message/PostgresMessage+RowDescription.swift deleted file mode 100644 index 5713cc99..00000000 --- a/Sources/PostgresNIO/Message/PostgresMessage+RowDescription.swift +++ /dev/null @@ -1,99 +0,0 @@ -import NIOCore - -extension PostgresMessage { - /// Identifies the message as a row description. - public struct RowDescription { - /// Describes a single field returns in a `RowDescription` message. - public struct Field: CustomStringConvertible { - static func parse(from buffer: inout ByteBuffer) throws -> Field { - guard let name = buffer.readNullTerminatedString() else { - throw PostgresError.protocol("Could not read row description field name") - } - guard let tableOID = buffer.readInteger(as: UInt32.self) else { - throw PostgresError.protocol("Could not read row description field table OID") - } - guard let columnAttributeNumber = buffer.readInteger(as: Int16.self) else { - throw PostgresError.protocol("Could not read row description field column attribute number") - } - guard let dataType = buffer.readInteger(as: PostgresDataType.self) else { - throw PostgresError.protocol("Could not read row description field data type") - } - guard let dataTypeSize = buffer.readInteger(as: Int16.self) else { - throw PostgresError.protocol("Could not read row description field data type size") - } - guard let dataTypeModifier = buffer.readInteger(as: Int32.self) else { - throw PostgresError.protocol("Could not read row description field data type modifier") - } - guard let formatCode = buffer.readInteger(as: PostgresFormat.self) else { - throw PostgresError.protocol("Could not read row description field format code") - } - return .init( - name: name, - tableOID: tableOID, - columnAttributeNumber: columnAttributeNumber, - dataType: dataType, - dataTypeSize: dataTypeSize, - dataTypeModifier: dataTypeModifier, - formatCode: formatCode - ) - } - - /// The field name. - public var name: String - - /// If the field can be identified as a column of a specific table, the object ID of the table; otherwise zero. - public var tableOID: UInt32 - - /// If the field can be identified as a column of a specific table, the attribute number of the column; otherwise zero. - public var columnAttributeNumber: Int16 - - /// The object ID of the field's data type. - public var dataType: PostgresDataType - - /// The data type size (see pg_type.typlen). Note that negative values denote variable-width types. - public var dataTypeSize: Int16 - - /// The type modifier (see pg_attribute.atttypmod). The meaning of the modifier is type-specific. - public var dataTypeModifier: Int32 - - /// The format code being used for the field. - /// Currently will be zero (text) or one (binary). - /// In a RowDescription returned from the statement variant of Describe, - /// the format code is not yet known and will always be zero. - public var formatCode: PostgresFormat - - /// See `CustomStringConvertible`. - public var description: String { - return self.name.description + "(\(tableOID))" - } - } - - - - /// The fields supplied in the row description. - public var fields: [Field] - - /// See `CustomStringConverible`. - public var description: String { - return "Row(\(self.fields)" - } - } -} - -@available(*, deprecated, message: "Deprecating conformance to `PostgresMessageType` since it is deprecated.") -extension PostgresMessage.RowDescription: PostgresMessageType { - /// See `PostgresMessageType`. - public static var identifier: PostgresMessage.Identifier { - return .rowDescription - } - - /// Parses an instance of this message type from a byte buffer. - public static func parse(from buffer: inout ByteBuffer) throws -> Self { - guard let fields = try buffer.read(array: Field.self, { buffer in - return try.parse(from: &buffer) - }) else { - throw PostgresError.protocol("Could not read row description fields") - } - return .init(fields: fields) - } -} diff --git a/Sources/PostgresNIO/Message/PostgresMessageType.swift b/Sources/PostgresNIO/Message/PostgresMessageType.swift deleted file mode 100644 index 170c4aec..00000000 --- a/Sources/PostgresNIO/Message/PostgresMessageType.swift +++ /dev/null @@ -1,36 +0,0 @@ -import NIOCore - -@available(*, deprecated, message: "Will be removed from public API. Internally we now use `PostgresBackendMessage` and `PostgresFrontendMessage`") -public protocol PostgresMessageType { - static var identifier: PostgresMessage.Identifier { get } - static func parse(from buffer: inout ByteBuffer) throws -> Self - func serialize(into buffer: inout ByteBuffer) throws -} - -@available(*, deprecated, message: "`PostgresMessageType` protocol is deprecated.") -extension PostgresMessageType { - @available(*, deprecated, message: "Will be removed from public API.") - func message() throws -> PostgresMessage { - var buffer = ByteBufferAllocator().buffer(capacity: 0) - try self.serialize(into: &buffer) - return .init(identifier: Self.identifier, data: buffer) - } - - public init(message: PostgresMessage) throws { - var message = message - self = try Self.parse(from: &message.data) - } - - @available(*, deprecated, message: "Will be removed from public API.") - public static var identifier: PostgresMessage.Identifier { - return .none - } - - public static func parse(from buffer: inout ByteBuffer) throws -> Self { - fatalError("\(Self.self) does not support parsing.") - } - - public func serialize(into buffer: inout ByteBuffer) throws { - fatalError("\(Self.self) does not support serializing.") - } -} diff --git a/Sources/PostgresNIO/New/Connection State Machine/AuthenticationStateMachine.swift b/Sources/PostgresNIO/New/Connection State Machine/AuthenticationStateMachine.swift index 245e5efd..d4d605ec 100644 --- a/Sources/PostgresNIO/New/Connection State Machine/AuthenticationStateMachine.swift +++ b/Sources/PostgresNIO/New/Connection State Machine/AuthenticationStateMachine.swift @@ -11,7 +11,7 @@ struct AuthenticationStateMachine { case saslChallengeResponseSent(SASLAuthenticationManager<SASLMechanism.SCRAM.SHA256>) case saslFinalReceived - case error(PSQLError) + case error(PostgresError) case authenticated } @@ -23,7 +23,7 @@ struct AuthenticationStateMachine { case wait case authenticated - case reportAuthenticationError(PSQLError) + case reportAuthenticationError(PostgresError) } let authContext: AuthContext @@ -51,7 +51,7 @@ struct AuthenticationStateMachine { return .authenticated case .md5(let salt): guard self.authContext.password != nil else { - return self.setAndFireError(PSQLError(code: .authMechanismRequiresPassword)) + return self.setAndFireError(PostgresError(code: .authMechanismRequiresPassword)) } self.state = .passwordAuthenticationSent return .sendPassword(.md5(salt: salt), self.authContext) @@ -160,11 +160,11 @@ struct AuthenticationStateMachine { return self.setAndFireError(.server(message)) } - mutating func errorHappened(_ error: PSQLError) -> Action { + mutating func errorHappened(_ error: PostgresError) -> Action { return self.setAndFireError(error) } - private mutating func setAndFireError(_ error: PSQLError) -> Action { + private mutating func setAndFireError(_ error: PostgresError) -> Action { switch self.state { case .initialized: preconditionFailure(""" diff --git a/Sources/PostgresNIO/New/Connection State Machine/CloseStateMachine.swift b/Sources/PostgresNIO/New/Connection State Machine/CloseStateMachine.swift index 791cebdd..299f04df 100644 --- a/Sources/PostgresNIO/New/Connection State Machine/CloseStateMachine.swift +++ b/Sources/PostgresNIO/New/Connection State Machine/CloseStateMachine.swift @@ -6,13 +6,13 @@ struct CloseStateMachine { case closeSyncSent(CloseCommandContext) case closeCompleteReceived - case error(PSQLError) + case error(PostgresError) } enum Action { case sendCloseSync(CloseTarget) case succeedClose(CloseCommandContext) - case failClose(CloseCommandContext, with: PSQLError) + case failClose(CloseCommandContext, with: PostgresError) case read case wait @@ -44,7 +44,7 @@ struct CloseStateMachine { } mutating func errorReceived(_ errorMessage: PostgresBackendMessage.ErrorResponse) -> Action { - let error = PSQLError.server(errorMessage) + let error = PostgresError.server(errorMessage) switch self.state { case .initialized: return self.setAndFireError(.unexpectedBackendMessage(.error(errorMessage))) @@ -63,7 +63,7 @@ struct CloseStateMachine { } } - mutating func errorHappened(_ error: PSQLError) -> Action { + mutating func errorHappened(_ error: PostgresError) -> Action { return self.setAndFireError(error) } @@ -84,7 +84,7 @@ struct CloseStateMachine { // MARK: Private Methods - private mutating func setAndFireError(_ error: PSQLError) -> Action { + private mutating func setAndFireError(_ error: PostgresError) -> Action { switch self.state { case .closeSyncSent(let closeContext): self.state = .error(error) diff --git a/Sources/PostgresNIO/New/Connection State Machine/ConnectionStateMachine.swift b/Sources/PostgresNIO/New/Connection State Machine/ConnectionStateMachine.swift index 9d264bcc..41c0f20c 100644 --- a/Sources/PostgresNIO/New/Connection State Machine/ConnectionStateMachine.swift +++ b/Sources/PostgresNIO/New/Connection State Machine/ConnectionStateMachine.swift @@ -33,8 +33,8 @@ struct ConnectionStateMachine { case extendedQuery(ExtendedQueryStateMachine, ConnectionContext) case closeCommand(CloseStateMachine, ConnectionContext) - case closing(PSQLError?) - case closed(clientInitiated: Bool, error: PSQLError?) + case closing(PostgresError?) + case closed(clientInitiated: Bool, error: PostgresError?) case modifying } @@ -57,7 +57,7 @@ struct ConnectionStateMachine { /// Tasks to fail with the error let tasks: [PSQLTask] - let error: PSQLError + let error: PostgresError let closePromise: EventLoopPromise<Void>? } @@ -87,24 +87,24 @@ struct ConnectionStateMachine { // --- general actions case sendParseDescribeBindExecuteSync(PostgresQuery) case sendBindExecuteSync(PSQLExecuteStatement) - case failQuery(EventLoopPromise<PSQLRowStream>, with: PSQLError, cleanupContext: CleanUpContext?) + case failQuery(EventLoopPromise<PSQLRowStream>, with: PostgresError, cleanupContext: CleanUpContext?) case succeedQuery(EventLoopPromise<PSQLRowStream>, with: QueryResult) // --- streaming actions // actions if query has requested next row but we are waiting for backend case forwardRows([DataRow]) case forwardStreamComplete([DataRow], commandTag: String) - case forwardStreamError(PSQLError, read: Bool, cleanupContext: CleanUpContext?) + case forwardStreamError(PostgresError, read: Bool, cleanupContext: CleanUpContext?) // Prepare statement actions case sendParseDescribeSync(name: String, query: String, bindingDataTypes: [PostgresDataType]) case succeedPreparedStatementCreation(EventLoopPromise<RowDescription?>, with: RowDescription?) - case failPreparedStatementCreation(EventLoopPromise<RowDescription?>, with: PSQLError, cleanupContext: CleanUpContext?) + case failPreparedStatementCreation(EventLoopPromise<RowDescription?>, with: PostgresError, cleanupContext: CleanUpContext?) // Close actions case sendCloseSync(CloseTarget) case succeedClose(CloseCommandContext) - case failClose(CloseCommandContext, with: PSQLError, cleanupContext: CleanUpContext?) + case failClose(CloseCommandContext, with: PostgresError, cleanupContext: CleanUpContext?) } private var state: State @@ -443,7 +443,7 @@ struct ConnectionStateMachine { } } - mutating func errorHappened(_ error: PSQLError) -> ConnectionAction { + mutating func errorHappened(_ error: PostgresError) -> ConnectionAction { switch self.state { case .initialized, .sslRequestSent, @@ -544,12 +544,12 @@ struct ConnectionStateMachine { } mutating func enqueue(task: PSQLTask) -> ConnectionAction { - let psqlErrror: PSQLError + let psqlErrror: PostgresError // check if we are quiescing. if so fail task immidiatly switch self.quiescingState { case .quiescing: - psqlErrror = PSQLError.clientClosedConnection(underlying: nil) + psqlErrror = PostgresError.clientClosedConnection(underlying: nil) case .notQuiescing: switch self.state { @@ -569,13 +569,13 @@ struct ConnectionStateMachine { return self.executeTask(task) case .closing(let error): - psqlErrror = PSQLError.clientClosedConnection(underlying: error) + psqlErrror = PostgresError.clientClosedConnection(underlying: error) case .closed(clientInitiated: true, error: let error): - psqlErrror = PSQLError.clientClosedConnection(underlying: error) + psqlErrror = PostgresError.clientClosedConnection(underlying: error) case .closed(clientInitiated: false, error: let error): - psqlErrror = PSQLError.serverClosedConnection(underlying: error) + psqlErrror = PostgresError.serverClosedConnection(underlying: error) case .modifying: preconditionFailure("Invalid state: \(self.state)") @@ -812,7 +812,7 @@ struct ConnectionStateMachine { return self.modify(with: action) } - private mutating func closeConnectionAndCleanup(_ error: PSQLError, closePromise: EventLoopPromise<Void>? = nil) -> ConnectionAction { + private mutating func closeConnectionAndCleanup(_ error: PostgresError, closePromise: EventLoopPromise<Void>? = nil) -> ConnectionAction { switch self.state { case .initialized, .sslRequestSent, @@ -956,7 +956,7 @@ struct ConnectionStateMachine { } extension ConnectionStateMachine { - func shouldCloseConnection(reason error: PSQLError) -> Bool { + func shouldCloseConnection(reason error: PostgresError) -> Bool { switch error.code.base { case .failedToAddSSLHandler, .receivedUnencryptedDataAfterSSLRequest, @@ -993,7 +993,7 @@ extension ConnectionStateMachine { } } - mutating func setErrorAndCreateCleanupContextIfNeeded(_ error: PSQLError) -> ConnectionAction.CleanUpContext? { + mutating func setErrorAndCreateCleanupContextIfNeeded(_ error: PostgresError) -> ConnectionAction.CleanUpContext? { if self.shouldCloseConnection(reason: error) { return self.setErrorAndCreateCleanupContext(error) } @@ -1001,7 +1001,7 @@ extension ConnectionStateMachine { return nil } - mutating func setErrorAndCreateCleanupContext(_ error: PSQLError, closePromise: EventLoopPromise<Void>? = nil) -> ConnectionAction.CleanUpContext { + mutating func setErrorAndCreateCleanupContext(_ error: PostgresError, closePromise: EventLoopPromise<Void>? = nil) -> ConnectionAction.CleanUpContext { let tasks = Array(self.taskQueue) self.taskQueue.removeAll() diff --git a/Sources/PostgresNIO/New/Connection State Machine/ExtendedQueryStateMachine.swift b/Sources/PostgresNIO/New/Connection State Machine/ExtendedQueryStateMachine.swift index 087a6c24..e5325030 100644 --- a/Sources/PostgresNIO/New/Connection State Machine/ExtendedQueryStateMachine.swift +++ b/Sources/PostgresNIO/New/Connection State Machine/ExtendedQueryStateMachine.swift @@ -20,7 +20,7 @@ struct ExtendedQueryStateMachine { case drain([RowDescription.Column]) case commandComplete(commandTag: String) - case error(PSQLError) + case error(PostgresError) case modifying } @@ -31,19 +31,19 @@ struct ExtendedQueryStateMachine { case sendBindExecuteSync(PSQLExecuteStatement) // --- general actions - case failQuery(EventLoopPromise<PSQLRowStream>, with: PSQLError) + case failQuery(EventLoopPromise<PSQLRowStream>, with: PostgresError) case succeedQuery(EventLoopPromise<PSQLRowStream>, with: QueryResult) - case evaluateErrorAtConnectionLevel(PSQLError) + case evaluateErrorAtConnectionLevel(PostgresError) case succeedPreparedStatementCreation(EventLoopPromise<RowDescription?>, with: RowDescription?) - case failPreparedStatementCreation(EventLoopPromise<RowDescription?>, with: PSQLError) + case failPreparedStatementCreation(EventLoopPromise<RowDescription?>, with: PostgresError) // --- streaming actions // actions if query has requested next row but we are waiting for backend case forwardRows([DataRow]) case forwardStreamComplete([DataRow], commandTag: String) - case forwardStreamError(PSQLError, read: Bool) + case forwardStreamError(PostgresError, read: Bool) case read case wait @@ -342,7 +342,7 @@ struct ExtendedQueryStateMachine { } mutating func errorReceived(_ errorMessage: PostgresBackendMessage.ErrorResponse) -> Action { - let error = PSQLError.server(errorMessage) + let error = PostgresError.server(errorMessage) switch self.state { case .initialized: return self.setAndFireError(.unexpectedBackendMessage(.error(errorMessage))) @@ -373,7 +373,7 @@ struct ExtendedQueryStateMachine { return .wait } - mutating func errorHappened(_ error: PSQLError) -> Action { + mutating func errorHappened(_ error: PostgresError) -> Action { return self.setAndFireError(error) } @@ -483,7 +483,7 @@ struct ExtendedQueryStateMachine { // MARK: Private Methods - private mutating func setAndFireError(_ error: PSQLError) -> Action { + private mutating func setAndFireError(_ error: PostgresError) -> Action { switch self.state { case .initialized(let context), .messagesSent(let context), diff --git a/Sources/PostgresNIO/New/Connection State Machine/PreparedStatementStateMachine.swift b/Sources/PostgresNIO/New/Connection State Machine/PreparedStatementStateMachine.swift index 5afa4d0b..6c24f437 100644 --- a/Sources/PostgresNIO/New/Connection State Machine/PreparedStatementStateMachine.swift +++ b/Sources/PostgresNIO/New/Connection State Machine/PreparedStatementStateMachine.swift @@ -4,7 +4,7 @@ struct PreparedStatementStateMachine { enum State { case preparing([PreparedStatementContext]) case prepared(RowDescription?) - case error(PSQLError) + case error(PostgresError) } var preparedStatements: [String: State] = [:] @@ -13,7 +13,7 @@ struct PreparedStatementStateMachine { case prepareStatement case waitForAlreadyInFlightPreparation case executeStatement(RowDescription?) - case returnError(PSQLError) + case returnError(PostgresError) } mutating func lookup(preparedStatement: PreparedStatementContext) -> LookupAction { @@ -72,10 +72,10 @@ struct PreparedStatementStateMachine { struct ErrorHappenedAction { var statements: [PreparedStatementContext] - var error: PSQLError + var error: PostgresError } - mutating func errorHappened(name: String, error: PSQLError) -> ErrorHappenedAction { + mutating func errorHappened(name: String, error: PostgresError) -> ErrorHappenedAction { guard let state = self.preparedStatements[name] else { fatalError("Unknown prepared statement \(name)") } diff --git a/Sources/PostgresNIO/New/NotificationListener.swift b/Sources/PostgresNIO/New/NotificationListener.swift index 2f784e33..4133321a 100644 --- a/Sources/PostgresNIO/New/NotificationListener.swift +++ b/Sources/PostgresNIO/New/NotificationListener.swift @@ -13,7 +13,6 @@ final class NotificationListener: @unchecked Sendable { case streamInitialized(CheckedContinuation<PostgresNotificationSequence, Error>) case streamListening(AsyncThrowingStream<PostgresNotification, Error>.Continuation) - case closure(PostgresListenContext, (PostgresListenContext, PostgresMessage.NotificationResponse) -> Void) case done } @@ -29,19 +28,6 @@ final class NotificationListener: @unchecked Sendable { self.state = .streamInitialized(checkedContinuation) } - init( - channel: String, - id: Int, - eventLoop: EventLoop, - context: PostgresListenContext, - closure: @Sendable @escaping (PostgresListenContext, PostgresMessage.NotificationResponse) -> Void - ) { - self.channel = channel - self.id = id - self.eventLoop = eventLoop - self.state = .closure(context, closure) - } - func startListeningSucceeded(handler: PostgresChannelHandler) { self.eventLoop.preconditionInEventLoop() let handlerLoopBound = NIOLoopBound(handler, eventLoop: self.eventLoop) @@ -73,9 +59,6 @@ final class NotificationListener: @unchecked Sendable { case .streamListening, .done: fatalError("Invalid state: \(self.state)") - - case .closure: - break // ignore } } @@ -87,14 +70,6 @@ final class NotificationListener: @unchecked Sendable { fatalError("Invalid state: \(self.state)") case .streamListening(let continuation): continuation.yield(.init(payload: backendMessage.payload)) - - case .closure(let postgresListenContext, let closure): - let message = PostgresMessage.NotificationResponse( - backendPID: backendMessage.backendPID, - channel: backendMessage.channel, - payload: backendMessage.payload - ) - closure(postgresListenContext, message) } } @@ -110,10 +85,6 @@ final class NotificationListener: @unchecked Sendable { self.state = .done continuation.finish(throwing: error) - case .closure(let postgresListenContext, _): - self.state = .done - postgresListenContext.cancel() - case .done: break // ignore } @@ -125,16 +96,12 @@ final class NotificationListener: @unchecked Sendable { switch self.state { case .streamInitialized(let checkedContinuation): self.state = .done - checkedContinuation.resume(throwing: PSQLError(code: .queryCancelled)) + checkedContinuation.resume(throwing: PostgresError(code: .queryCancelled)) case .streamListening(let continuation): self.state = .done continuation.finish() - case .closure(let postgresListenContext, _): - self.state = .done - postgresListenContext.cancel() - case .done: break // ignore } diff --git a/Sources/PostgresNIO/New/PSQLEventsHandler.swift b/Sources/PostgresNIO/New/PSQLEventsHandler.swift index 0f426f20..82db5ffc 100644 --- a/Sources/PostgresNIO/New/PSQLEventsHandler.swift +++ b/Sources/PostgresNIO/New/PSQLEventsHandler.swift @@ -3,11 +3,6 @@ import NIOTLS import Logging enum PSQLOutgoingEvent { - /// the event we send down the channel to inform the ``PostgresChannelHandler`` to authenticate - /// - /// this shall be removed with the next breaking change and always supplied with `PSQLConnection.Configuration` - case authenticate(AuthContext) - case gracefulShutdown } diff --git a/Sources/PostgresNIO/New/PSQLTask.swift b/Sources/PostgresNIO/New/PSQLTask.swift index 6106fd21..737d048b 100644 --- a/Sources/PostgresNIO/New/PSQLTask.swift +++ b/Sources/PostgresNIO/New/PSQLTask.swift @@ -13,7 +13,7 @@ enum PSQLTask { case extendedQuery(ExtendedQueryContext) case closeCommand(CloseCommandContext) - func failWithError(_ error: PSQLError) { + func failWithError(_ error: PostgresError) { switch self { case .extendedQuery(let extendedQueryContext): switch extendedQueryContext.query { diff --git a/Sources/PostgresNIO/New/PostgresChannelHandler.swift b/Sources/PostgresNIO/New/PostgresChannelHandler.swift index 0a14849a..2c0b5047 100644 --- a/Sources/PostgresNIO/New/PostgresChannelHandler.swift +++ b/Sources/PostgresNIO/New/PostgresChannelHandler.swift @@ -277,10 +277,6 @@ final class PostgresChannelHandler: ChannelDuplexHandler { self.logger.trace("User outbound event received", metadata: [.userEvent: "\(event)"]) switch event { - case PSQLOutgoingEvent.authenticate(let authContext): - let action = self.state.provideAuthenticationContext(authContext) - self.run(action, with: context) - case PSQLOutgoingEvent.gracefulShutdown: let action = self.state.gracefulClose(promise) self.run(action, with: context) @@ -385,17 +381,15 @@ final class PostgresChannelHandler: ChannelDuplexHandler { case .provideAuthenticationContext: context.fireUserInboundEventTriggered(PSQLEvent.readyForStartup) - - if let username = self.configuration.username { - let authContext = AuthContext( - username: username, - password: self.configuration.password, - database: self.configuration.database, - additionalParameters: self.configuration.options.additionalStartupParameters - ) - let action = self.state.provideAuthenticationContext(authContext) - return self.run(action, with: context) - } + let authContext = AuthContext( + username: self.configuration.username, + password: self.configuration.password, + database: self.configuration.database, + additionalParameters: self.configuration.options.additionalStartupParameters + ) + let action = self.state.provideAuthenticationContext(authContext) + return self.run(action, with: context) + case .fireEventReadyForQuery: context.fireUserInboundEventTriggered(PSQLEvent.readyForQuery) case .closeConnection(let promise): @@ -624,12 +618,12 @@ final class PostgresChannelHandler: ChannelDuplexHandler { } case .failure(let error): - let finalError: PSQLError - if var psqlError = error as? PSQLError { + let finalError: PostgresError + if var psqlError = error as? PostgresError { psqlError.code = .listenFailed finalError = psqlError } else { - var psqlError = PSQLError(code: .listenFailed) + var psqlError = PostgresError(code: .listenFailed) psqlError.underlying = error finalError = psqlError } @@ -710,8 +704,8 @@ final class PostgresChannelHandler: ChannelDuplexHandler { context: context ) case .failure(let error): - let psqlError: PSQLError - if let error = error as? PSQLError { + let psqlError: PostgresError + if let error = error as? PostgresError { psqlError = error } else { psqlError = .connectionError(underlying: error) @@ -773,7 +767,7 @@ final class PostgresChannelHandler: ChannelDuplexHandler { private func prepareStatementFailed( name: String, - error: PSQLError, + error: PostgresError, context: ChannelHandlerContext ) { let action = self.preparedStatementState.errorHappened( diff --git a/Sources/PostgresNIO/New/PSQLError.swift b/Sources/PostgresNIO/New/PostgresError.swift similarity index 92% rename from Sources/PostgresNIO/New/PSQLError.swift rename to Sources/PostgresNIO/New/PostgresError.swift index 4a9f9216..7baaf6f8 100644 --- a/Sources/PostgresNIO/New/PSQLError.swift +++ b/Sources/PostgresNIO/New/PostgresError.swift @@ -2,7 +2,7 @@ import NIOCore /// An error that is thrown from the PostgresClient. /// Sendability enforced through Copy on Write semantics -public struct PSQLError: Error, @unchecked Sendable { +public struct PostgresError: Error, @unchecked Sendable { public struct Code: Sendable, Hashable, CustomStringConvertible { enum Base: Sendable, Hashable { @@ -115,7 +115,7 @@ public struct PSQLError: Error, @unchecked Sendable { } } - /// The ``PSQLError/Code-swift.struct`` code + /// The ``PostgresError/Code-swift.struct`` code public internal(set) var code: Code { get { self.backing.code } set { @@ -390,66 +390,66 @@ public struct PSQLError: Error, @unchecked Sendable { return new } - static func clientClosedConnection(underlying: Error?) -> PSQLError { - var error = PSQLError(code: .clientClosedConnection) + static func clientClosedConnection(underlying: Error?) -> PostgresError { + var error = PostgresError(code: .clientClosedConnection) error.underlying = underlying return error } - static func serverClosedConnection(underlying: Error?) -> PSQLError { - var error = PSQLError(code: .serverClosedConnection) + static func serverClosedConnection(underlying: Error?) -> PostgresError { + var error = PostgresError(code: .serverClosedConnection) error.underlying = underlying return error } - static let authMechanismRequiresPassword = PSQLError(code: .authMechanismRequiresPassword) + static let authMechanismRequiresPassword = PostgresError(code: .authMechanismRequiresPassword) - static let sslUnsupported = PSQLError(code: .sslUnsupported) + static let sslUnsupported = PostgresError(code: .sslUnsupported) - static let queryCancelled = PSQLError(code: .queryCancelled) + static let queryCancelled = PostgresError(code: .queryCancelled) - static let uncleanShutdown = PSQLError(code: .uncleanShutdown) + static let uncleanShutdown = PostgresError(code: .uncleanShutdown) - static let receivedUnencryptedDataAfterSSLRequest = PSQLError(code: .receivedUnencryptedDataAfterSSLRequest) + static let receivedUnencryptedDataAfterSSLRequest = PostgresError(code: .receivedUnencryptedDataAfterSSLRequest) - static func server(_ response: PostgresBackendMessage.ErrorResponse) -> PSQLError { - var error = PSQLError(code: .server) + static func server(_ response: PostgresBackendMessage.ErrorResponse) -> PostgresError { + var error = PostgresError(code: .server) error.serverInfo = .init(response) return error } - static func sasl(underlying: Error) -> PSQLError { - var error = PSQLError(code: .saslError) + static func sasl(underlying: Error) -> PostgresError { + var error = PostgresError(code: .saslError) error.underlying = underlying return error } - static func failedToAddSSLHandler(underlying: Error) -> PSQLError { - var error = PSQLError(code: .failedToAddSSLHandler) + static func failedToAddSSLHandler(underlying: Error) -> PostgresError { + var error = PostgresError(code: .failedToAddSSLHandler) error.underlying = underlying return error } - static func connectionError(underlying: Error) -> PSQLError { - var error = PSQLError(code: .connectionError) + static func connectionError(underlying: Error) -> PostgresError { + var error = PostgresError(code: .connectionError) error.underlying = underlying return error } - static func unsupportedAuthMechanism(_ authScheme: UnsupportedAuthScheme) -> PSQLError { - var error = PSQLError(code: .unsupportedAuthMechanism) + static func unsupportedAuthMechanism(_ authScheme: UnsupportedAuthScheme) -> PostgresError { + var error = PostgresError(code: .unsupportedAuthMechanism) error.unsupportedAuthScheme = authScheme return error } - static func invalidCommandTag(_ value: String) -> PSQLError { - var error = PSQLError(code: .invalidCommandTag) + static func invalidCommandTag(_ value: String) -> PostgresError { + var error = PostgresError(code: .invalidCommandTag) error.invalidCommandTag = value return error } - static func unlistenError(underlying: Error) -> PSQLError { - var error = PSQLError(code: .unlistenFailed) + static func unlistenError(underlying: Error) -> PostgresError { + var error = PostgresError(code: .unlistenFailed) error.underlying = underlying return error } @@ -465,12 +465,12 @@ public struct PSQLError: Error, @unchecked Sendable { case sasl(mechanisms: [String]) } - static var poolClosed: PSQLError { + static var poolClosed: PostgresError { Self.init(code: .poolClosed) } } -extension PSQLError: CustomStringConvertible { +extension PostgresError: CustomStringConvertible { public var description: String { // This may seem very odd... But we are afraid that users might accidentally send the // unfiltered errors out to end-users. This may leak security relevant information. For this @@ -481,7 +481,7 @@ extension PSQLError: CustomStringConvertible { } } -extension PSQLError: CustomDebugStringConvertible { +extension PostgresError: CustomDebugStringConvertible { public var debugDescription: String { var result = #"PSQLError(code: \#(self.code)"# @@ -490,7 +490,7 @@ extension PSQLError: CustomDebugStringConvertible { result.append( serverInfo.fields .sorted(by: { $0.key.rawValue < $1.key.rawValue }) - .map { "\(PSQLError.ServerInfo.Field($0.0)): \($0.1)" } + .map { "\(PostgresError.ServerInfo.Field($0.0)): \($0.1)" } .joined(separator: ", ") ) result.append("]") diff --git a/Sources/PostgresNIO/New/PostgresQuery.swift b/Sources/PostgresNIO/New/PostgresQuery.swift index 6449ab29..b5570693 100644 --- a/Sources/PostgresNIO/New/PostgresQuery.swift +++ b/Sources/PostgresNIO/New/PostgresQuery.swift @@ -258,17 +258,6 @@ public struct PostgresBindings: Sendable, Hashable { value.encodeRaw(into: &self.bytes, context: context) self.metadata.append(.init(value: value, protected: false)) } - - public mutating func append(_ postgresData: PostgresData) { - switch postgresData.value { - case .none: - self.bytes.writeInteger(-1, as: Int32.self) - case .some(var input): - self.bytes.writeInteger(Int32(input.readableBytes)) - self.bytes.writeBuffer(&input) - } - self.metadata.append(.init(dataType: postgresData.type, format: .binary, protected: true)) - } } extension PostgresBindings: CustomStringConvertible, CustomDebugStringConvertible { diff --git a/Sources/PostgresNIO/Pool/PostgresClient.swift b/Sources/PostgresNIO/Pool/PostgresClient.swift index ad8a4bf1..7539b130 100644 --- a/Sources/PostgresNIO/Pool/PostgresClient.swift +++ b/Sources/PostgresNIO/Pool/PostgresClient.swift @@ -327,7 +327,7 @@ public final class PostgresClient: Sendable, ServiceLifecycle.Service { let logger = logger ?? Self.loggingDisabled do { guard query.binds.count <= Int(UInt16.max) else { - throw PSQLError(code: .tooManyParameters, query: query, file: file, line: line) + throw PostgresError(code: .tooManyParameters, query: query, file: file, line: line) } let connection = try await self.leaseConnection() @@ -353,7 +353,7 @@ public final class PostgresClient: Sendable, ServiceLifecycle.Service { self.pool.releaseConnection(connection) }) }.get() - } catch var error as PSQLError { + } catch var error as PostgresError { error.file = file error.line = line error.query = query @@ -393,7 +393,7 @@ public final class PostgresClient: Sendable, ServiceLifecycle.Service { .map { $0.asyncSequence(onFinish: { self.pool.releaseConnection(connection) }) } .get() .map { try preparedStatement.decodeRow($0) } - } catch var error as PSQLError { + } catch var error as PostgresError { error.file = file error.line = line error.query = .init( @@ -462,7 +462,7 @@ struct PostgresKeepAliveBehavor: ConnectionKeepAliveBehavior { } func runKeepAlive(for connection: PostgresConnection) async throws { - try await connection.query(self.behavior!.query, logger: self.logger).map { _ in }.get() + try await connection.query(self.behavior!.query, logger: self.logger) } } @@ -489,14 +489,14 @@ extension PostgresConnection: PooledConnection { extension ConnectionPoolError { func mapToPSQLError(lastConnectError: Error?) -> Error { - var psqlError: PSQLError + var psqlError: PostgresError switch self { case .poolShutdown: - psqlError = PSQLError.poolClosed + psqlError = PostgresError.poolClosed psqlError.underlying = self case .requestCancelled: - psqlError = PSQLError.queryCancelled + psqlError = PostgresError.queryCancelled psqlError.underlying = self default: diff --git a/Sources/PostgresNIO/Postgres+PSQLCompat.swift b/Sources/PostgresNIO/Postgres+PSQLCompat.swift deleted file mode 100644 index 7d464c2b..00000000 --- a/Sources/PostgresNIO/Postgres+PSQLCompat.swift +++ /dev/null @@ -1,74 +0,0 @@ -import NIOCore - -extension PSQLError { - func toPostgresError() -> Error { - switch self.code.base { - case .queryCancelled: - return self - case .server, .listenFailed: - guard let serverInfo = self.serverInfo else { - return self - } - - var fields = [PostgresMessage.Error.Field: String]() - fields.reserveCapacity(serverInfo.underlying.fields.count) - serverInfo.underlying.fields.forEach { (key, value) in - fields[PostgresMessage.Error.Field(rawValue: key.rawValue)!] = value - } - return PostgresError.server(PostgresMessage.Error(fields: fields)) - case .sslUnsupported: - return PostgresError.protocol("Server does not support TLS") - case .failedToAddSSLHandler: - return self.underlying ?? self - case .messageDecodingFailure: - let message = self.underlying != nil ? String(describing: self.underlying!) : "no message" - return PostgresError.protocol("Error decoding message: \(message)") - case .unexpectedBackendMessage: - let message = self.backendMessage != nil ? String(describing: self.backendMessage!) : "no message" - return PostgresError.protocol("Unexpected message: \(message)") - case .unsupportedAuthMechanism: - let message = self.unsupportedAuthScheme != nil ? String(describing: self.unsupportedAuthScheme!) : "no scheme" - return PostgresError.protocol("Unsupported auth scheme: \(message)") - case .authMechanismRequiresPassword: - return PostgresError.protocol("Unable to authenticate without password") - case .receivedUnencryptedDataAfterSSLRequest: - return PostgresError.protocol("Received unencrypted data after SSL request") - case .saslError: - return self.underlying ?? self - case .tooManyParameters, .invalidCommandTag: - return self - case .clientClosedConnection, - .serverClosedConnection: - return PostgresError.connectionClosed - case .connectionError: - return self.underlying ?? self - case .unlistenFailed: - return self.underlying ?? self - case .uncleanShutdown: - return PostgresError.protocol("Unexpected connection close") - case .poolClosed: - return self - } - } -} - -extension PostgresFormat { - init(psqlFormatCode: PostgresFormat) { - switch psqlFormatCode { - case .binary: - self = .binary - case .text: - self = .text - } - } -} - -extension Error { - internal var asAppropriatePostgresError: Error { - if let psqlError = self as? PSQLError { - return psqlError.toPostgresError() - } else { - return self - } - } -} diff --git a/Sources/PostgresNIO/PostgresDatabase+Query.swift b/Sources/PostgresNIO/PostgresDatabase+Query.swift deleted file mode 100644 index 8de93814..00000000 --- a/Sources/PostgresNIO/PostgresDatabase+Query.swift +++ /dev/null @@ -1,106 +0,0 @@ -import NIOCore -import Logging -import NIOConcurrencyHelpers - -extension PostgresDatabase { - public func query( - _ string: String, - _ binds: [PostgresData] = [] - ) -> EventLoopFuture<PostgresQueryResult> { - let box = NIOLockedValueBox((metadata: PostgresQueryMetadata?.none, rows: [PostgresRow]())) - - return self.query(string, binds, onMetadata: { metadata in - box.withLockedValue { - $0.metadata = metadata - } - }) { row in - box.withLockedValue { - $0.rows.append(row) - } - }.map { - box.withLockedValue { - PostgresQueryResult(metadata: $0.metadata!, rows: $0.rows) - } - } - } - - @preconcurrency - public func query( - _ string: String, - _ binds: [PostgresData] = [], - onMetadata: @Sendable @escaping (PostgresQueryMetadata) -> () = { _ in }, - onRow: @Sendable @escaping (PostgresRow) throws -> () - ) -> EventLoopFuture<Void> { - var bindings = PostgresBindings(capacity: binds.count) - binds.forEach { bindings.append($0) } - let query = PostgresQuery(unsafeSQL: string, binds: bindings) - let request = PostgresCommands.query(query, onMetadata: onMetadata, onRow: onRow) - - return self.send(request, logger: logger) - } -} - -public struct PostgresQueryResult: Sendable { - public let metadata: PostgresQueryMetadata - public let rows: [PostgresRow] -} - -extension PostgresQueryResult: Collection { - public typealias Index = Int - public typealias Element = PostgresRow - - public var startIndex: Int { - self.rows.startIndex - } - - public var endIndex: Int { - self.rows.endIndex - } - - public subscript(position: Int) -> PostgresRow { - self.rows[position] - } - - public func index(after i: Int) -> Int { - self.rows.index(after: i) - } -} - -public struct PostgresQueryMetadata: Sendable { - public let command: String - public var oid: Int? - public var rows: Int? - - init?(string: String) { - let parts = string.split(separator: " ") - switch parts.first { - case "INSERT": - // INSERT oid rows - guard parts.count == 3 else { - return nil - } - self.command = .init(parts[0]) - self.oid = Int(parts[1]) - self.rows = Int(parts[2]) - case "SELECT" where parts.count == 1: - // AWS Redshift does not return the actual row count as defined in the postgres wire spec for SELECT: - // https://www.postgresql.org/docs/13/protocol-message-formats.html in section `CommandComplete` - self.command = "SELECT" - self.oid = nil - self.rows = nil - case "SELECT", "DELETE", "UPDATE", "MOVE", "FETCH", "COPY": - // <cmd> rows - guard parts.count == 2 else { - return nil - } - self.command = .init(parts[0]) - self.oid = nil - self.rows = Int(parts[1]) - default: - // <cmd> - self.command = string - self.oid = nil - self.rows = nil - } - } -} diff --git a/Sources/PostgresNIO/PostgresDatabase+SimpleQuery.swift b/Sources/PostgresNIO/PostgresDatabase+SimpleQuery.swift deleted file mode 100644 index 5cf2d7a4..00000000 --- a/Sources/PostgresNIO/PostgresDatabase+SimpleQuery.swift +++ /dev/null @@ -1,19 +0,0 @@ -import NIOCore -import NIOConcurrencyHelpers -import Logging - -extension PostgresDatabase { - public func simpleQuery(_ string: String) -> EventLoopFuture<[PostgresRow]> { - let rowsBoxed = NIOLockedValueBox([PostgresRow]()) - return self.simpleQuery(string) { row in - rowsBoxed.withLockedValue { - $0.append(row) - } - }.map { rowsBoxed.withLockedValue { $0 } } - } - - @preconcurrency - public func simpleQuery(_ string: String, _ onRow: @Sendable @escaping (PostgresRow) throws -> ()) -> EventLoopFuture<Void> { - self.query(string, onRow: onRow) - } -} diff --git a/Sources/PostgresNIO/PostgresDatabase.swift b/Sources/PostgresNIO/PostgresDatabase.swift deleted file mode 100644 index fcd1afc7..00000000 --- a/Sources/PostgresNIO/PostgresDatabase.swift +++ /dev/null @@ -1,39 +0,0 @@ -import NIOCore -import Logging - -@preconcurrency -public protocol PostgresDatabase: Sendable { - var logger: Logger { get } - var eventLoop: EventLoop { get } - func send( - _ request: PostgresRequest, - logger: Logger - ) -> EventLoopFuture<Void> - - func withConnection<T>(_ closure: @escaping (PostgresConnection) -> EventLoopFuture<T>) -> EventLoopFuture<T> -} - -extension PostgresDatabase { - public func logging(to logger: Logger) -> PostgresDatabase { - _PostgresDatabaseCustomLogger(database: self, logger: logger) - } -} - -private struct _PostgresDatabaseCustomLogger { - let database: PostgresDatabase - let logger: Logger -} - -extension _PostgresDatabaseCustomLogger: PostgresDatabase { - var eventLoop: EventLoop { - self.database.eventLoop - } - - func send(_ request: PostgresRequest, logger: Logger) -> EventLoopFuture<Void> { - self.database.send(request, logger: logger) - } - - func withConnection<T>(_ closure: @escaping (PostgresConnection) -> EventLoopFuture<T>) -> EventLoopFuture<T> { - self.database.withConnection(closure) - } -} diff --git a/Sources/PostgresNIO/PostgresQueryMetadata.swift b/Sources/PostgresNIO/PostgresQueryMetadata.swift new file mode 100644 index 00000000..28a8d977 --- /dev/null +++ b/Sources/PostgresNIO/PostgresQueryMetadata.swift @@ -0,0 +1,45 @@ +import NIOCore +import Logging +import NIOConcurrencyHelpers + +public struct PostgresQueryMetadata: Sendable { + public let command: String + public var oid: Int? + public var rows: Int? + + init?(string: String) { + let parts = string.split(separator: " ") + guard parts.count >= 1 else { + return nil + } + switch parts[0] { + case "INSERT": + // INSERT oid rows + guard parts.count == 3 else { + return nil + } + self.command = .init(parts[0]) + self.oid = Int(parts[1]) + self.rows = Int(parts[2]) + case "SELECT" where parts.count == 1: + // AWS Redshift does not return the actual row count as defined in the postgres wire spec for SELECT: + // https://www.postgresql.org/docs/13/protocol-message-formats.html in section `CommandComplete` + self.command = "SELECT" + self.oid = nil + self.rows = nil + case "SELECT", "DELETE", "UPDATE", "MOVE", "FETCH", "COPY": + // <cmd> rows + guard parts.count == 2 else { + return nil + } + self.command = .init(parts[0]) + self.oid = nil + self.rows = Int(parts[1]) + default: + // <cmd> + self.command = string + self.oid = nil + self.rows = nil + } + } +} diff --git a/Sources/PostgresNIO/PostgresRequest.swift b/Sources/PostgresNIO/PostgresRequest.swift deleted file mode 100644 index 71bb6cd1..00000000 --- a/Sources/PostgresNIO/PostgresRequest.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Logging - -/// Protocol to encapsulate a function call on the Postgres server -/// -/// This protocol is deprecated going forward. -public protocol PostgresRequest { - // return nil to end request - func respond(to message: PostgresMessage) throws -> [PostgresMessage]? - func start() throws -> [PostgresMessage] - func log(to logger: Logger) -} diff --git a/Sources/PostgresNIO/Utilities/Exports.swift b/Sources/PostgresNIO/Utilities/Exports.swift deleted file mode 100644 index 144ff3c9..00000000 --- a/Sources/PostgresNIO/Utilities/Exports.swift +++ /dev/null @@ -1,3 +0,0 @@ -@_documentation(visibility: internal) @_exported import NIO -@_documentation(visibility: internal) @_exported import NIOSSL -@_documentation(visibility: internal) @_exported import struct Logging.Logger diff --git a/Sources/PostgresNIO/Utilities/NIOUtils.swift b/Sources/PostgresNIO/Utilities/NIOUtils.swift index 75ab8c20..44172bf1 100644 --- a/Sources/PostgresNIO/Utilities/NIOUtils.swift +++ b/Sources/PostgresNIO/Utilities/NIOUtils.swift @@ -52,9 +52,3 @@ internal extension ByteBuffer { return array } } - -internal extension Sequence where Element == UInt8 { - func hexdigest() -> String { - return reduce("") { $0 + String(format: "%02x", $1) } - } -} diff --git a/Sources/PostgresNIO/Utilities/PostgresError+Code.swift b/Sources/PostgresNIO/Utilities/PostgresError+Code.swift index fae903fe..5a64f944 100644 --- a/Sources/PostgresNIO/Utilities/PostgresError+Code.swift +++ b/Sources/PostgresNIO/Utilities/PostgresError+Code.swift @@ -1,4 +1,5 @@ -extension PostgresError { + +extension PostgresError.ServerInfo { public struct Code: Sendable, ExpressibleByStringLiteral, Equatable { // Class 00 — Successful Completion public static let successfulCompletion: Code = "00000" @@ -334,16 +335,4 @@ extension PostgresError { self.raw = raw } } - - public var code: Code { - switch self { - case .protocol: return .internalError - case .server(let server): - guard let code = server.fields[.sqlState] else { - return .internalError - } - return Code(raw: code) - case .connectionClosed: return .internalError - } - } } diff --git a/Sources/PostgresNIO/Utilities/PostgresError.swift b/Sources/PostgresNIO/Utilities/PostgresError.swift deleted file mode 100644 index b9524275..00000000 --- a/Sources/PostgresNIO/Utilities/PostgresError.swift +++ /dev/null @@ -1,26 +0,0 @@ -import Foundation - -public enum PostgresError: Error, LocalizedError, CustomStringConvertible { - case `protocol`(String) - case server(PostgresMessage.Error) - case connectionClosed - - /// See `LocalizedError`. - public var errorDescription: String? { - return self.description - } - - /// See `CustomStringConvertible`. - public var description: String { - let description: String - switch self { - case .protocol(let message): - description = "protocol error: \(message)" - case .server(let error): - return "server: \(error.description)" - case .connectionClosed: - description = "connection closed" - } - return "NIOPostgres error: \(description)" - } -} diff --git a/Sources/PostgresNIO/Utilities/SASLAuthenticationManager.swift b/Sources/PostgresNIO/Utilities/SASLAuthenticationManager.swift index f6c7ee75..1ae314e6 100644 --- a/Sources/PostgresNIO/Utilities/SASLAuthenticationManager.swift +++ b/Sources/PostgresNIO/Utilities/SASLAuthenticationManager.swift @@ -1,6 +1,6 @@ import Crypto -public final class SASLAuthenticationManager<M: SASLAuthenticationMechanism> { +final class SASLAuthenticationManager<M: SASLAuthenticationMechanism> { private enum Role { case client, server @@ -24,12 +24,12 @@ public final class SASLAuthenticationManager<M: SASLAuthenticationMechanism> { private let role: Role private var state: State = .waitingForInitial - public init(asClientSpeaking mechanism: M) { + init(asClientSpeaking mechanism: M) { self.mechanism = mechanism self.role = .client } - public init(asServerAccepting mechanism: M) { + init(asServerAccepting mechanism: M) { self.mechanism = mechanism self.role = .server } @@ -46,7 +46,7 @@ public final class SASLAuthenticationManager<M: SASLAuthenticationMechanism> { /// /// Pass a `nil` message to start the initial request from a client. It is /// invalid to do this for a server. - public func handle(message: [UInt8]?, sender: ([UInt8]) throws -> Void) throws -> Bool { + func handle(message: [UInt8]?, sender: ([UInt8]) throws -> Void) throws -> Bool { guard self.state != .done else { // Already did whatever we were gonna do. throw SASLAuthenticationError.resultAlreadyDelivered @@ -99,7 +99,7 @@ public final class SASLAuthenticationManager<M: SASLAuthenticationMechanism> { /// Various errors that can occur during SASL negotiation that are not specific /// to the particular SASL mechanism in use. -public enum SASLAuthenticationError: Error { +enum SASLAuthenticationError: Error { /// A server can not handle a nonexistent message. Only an initial-state /// client can do that, and even then it's really just a proxy for the API /// having difficulty expressing "this must be done once and then never @@ -128,7 +128,7 @@ public enum SASLAuthenticationError: Error { /// Signifies an action to be taken as the result of a single step of a SASL /// mechanism. -public enum SASLAuthenticationStepResult { +enum SASLAuthenticationStepResult { /// More steps are needed. Assume neither success nor failure. If data is /// provided, send it. A value of `nil` signifies sending no response at @@ -155,7 +155,7 @@ public enum SASLAuthenticationStepResult { /// the responsibility of each individual implementation to provide an API for /// creating instances of itself which are able to retrieve information from the /// caller (such as usernames and passwords) by some mechanism. -public protocol SASLAuthenticationMechanism { +protocol SASLAuthenticationMechanism { /// The IANA-registered SASL mechanism name. This may be a family prefix or /// a specific mechanism name. It is explicitly suitable for use in diff --git a/Tests/IntegrationTests/AsyncTests.swift b/Tests/IntegrationTests/AsyncTests.swift index b4c8e93f..7f7d7945 100644 --- a/Tests/IntegrationTests/AsyncTests.swift +++ b/Tests/IntegrationTests/AsyncTests.swift @@ -134,7 +134,7 @@ final class AsyncPostgresConnectionTests: XCTestCase { } XCTFail("Expected to get cancelled while reading the query") } catch { - guard let error = error as? PSQLError else { return XCTFail("Unexpected error type") } + guard let error = error as? PostgresError else { return XCTFail("Unexpected error type") } XCTAssertEqual(error.code, .server) XCTAssertEqual(error.serverInfo?[.severity], "ERROR") @@ -164,7 +164,7 @@ final class AsyncPostgresConnectionTests: XCTestCase { try await connection.query("SELECT generte_series(\(start), \(end));", logger: .psqlTest) XCTFail("Expected to throw from the request") } catch { - guard let error = error as? PSQLError else { return XCTFail("Unexpected error type: \(error)") } + guard let error = error as? PostgresError else { return XCTFail("Unexpected error type: \(error)") } XCTAssertEqual(error.code, .server) XCTAssertEqual(error.serverInfo?[.severity], "ERROR") diff --git a/Tests/IntegrationTests/PSQLIntegrationTests.swift b/Tests/IntegrationTests/PSQLIntegrationTests.swift index d541899b..1c092903 100644 --- a/Tests/IntegrationTests/PSQLIntegrationTests.swift +++ b/Tests/IntegrationTests/PSQLIntegrationTests.swift @@ -40,343 +40,342 @@ final class IntegrationTests: XCTestCase { var connection: PostgresConnection? XCTAssertThrowsError(connection = try PostgresConnection.connect(on: eventLoopGroup.next(), configuration: config, id: 1, logger: logger).wait()) { - XCTAssertTrue($0 is PSQLError) + XCTAssertTrue($0 is PostgresError) } // In case of a test failure the created connection must be closed. XCTAssertNoThrow(try connection?.close().wait()) } - func testQueryVersion() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let eventLoop = eventLoopGroup.next() - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow(try conn?.close().wait()) } - - var result: PostgresQueryResult? - XCTAssertNoThrow(result = try conn?.query("SELECT version()", logger: .psqlTest).wait()) - let rows = result?.rows - var version: String? - XCTAssertNoThrow(version = try rows?.first?.decode(String.self, context: .default)) - XCTAssertEqual(version?.contains("PostgreSQL"), true) - } - - func testQuery10kItems() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let eventLoop = eventLoopGroup.next() - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow(try conn?.close().wait()) } - - var metadata: PostgresQueryMetadata? - let received = ManagedAtomic<Int64>(0) - XCTAssertNoThrow(metadata = try conn?.query("SELECT generate_series(1, 10000);", logger: .psqlTest) { row in - func workaround() { - let expected = received.wrappingIncrementThenLoad(ordering: .relaxed) - XCTAssertEqual(expected, try row.decode(Int64.self, context: .default)) - } - - workaround() - }.wait()) - - XCTAssertEqual(received.load(ordering: .relaxed), 10000) - XCTAssertEqual(metadata?.command, "SELECT") - XCTAssertEqual(metadata?.rows, 10000) - } - - func test1kRoundTrips() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let eventLoop = eventLoopGroup.next() - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow(try conn?.close().wait()) } - - for _ in 0..<1_000 { - var result: PostgresQueryResult? - XCTAssertNoThrow(result = try conn?.query("SELECT version()", logger: .psqlTest).wait()) - var version: String? - XCTAssertNoThrow(version = try result?.rows.first?.decode(String.self, context: .default)) - XCTAssertEqual(version?.contains("PostgreSQL"), true) - } - } - - func testQuerySelectParameter() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let eventLoop = eventLoopGroup.next() - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow(try conn?.close().wait()) } - - var result: PostgresQueryResult? - XCTAssertNoThrow(result = try conn?.query("SELECT \("hello")::TEXT as foo", logger: .psqlTest).wait()) - var foo: String? - XCTAssertNoThrow(foo = try result?.rows.first?.decode(String.self, context: .default)) - XCTAssertEqual(foo, "hello") - } - - func testQueryNothing() throws { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let eventLoop = eventLoopGroup.next() - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow(try conn?.close().wait()) } - - var _result: PostgresQueryResult? - XCTAssertNoThrow(_result = try conn?.query(""" - -- Some comments - """, logger: .psqlTest).wait()) - - let result = try XCTUnwrap(_result) - XCTAssertEqual(result.rows, []) - XCTAssertEqual(result.metadata.command, "") - } - - func testDecodeIntegers() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let eventLoop = eventLoopGroup.next() - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow(try conn?.close().wait()) } - - var result: PostgresQueryResult? - XCTAssertNoThrow(result = try conn?.query(""" - SELECT - 1::SMALLINT as smallint, - -32767::SMALLINT as smallint_min, - 32767::SMALLINT as smallint_max, - 1::INT as int, - -2147483647::INT as int_min, - 2147483647::INT as int_max, - 1::BIGINT as bigint, - -9223372036854775807::BIGINT as bigint_min, - 9223372036854775807::BIGINT as bigint_max - """, logger: .psqlTest).wait()) - - XCTAssertEqual(result?.rows.count, 1) - let row = result?.rows.first - - var cells: (Int16, Int16, Int16, Int32, Int32, Int32, Int64, Int64, Int64)? - XCTAssertNoThrow(cells = try row?.decode((Int16, Int16, Int16, Int32, Int32, Int32, Int64, Int64, Int64).self, context: .default)) - - XCTAssertEqual(cells?.0, 1) - XCTAssertEqual(cells?.1, -32_767) - XCTAssertEqual(cells?.2, 32_767) - XCTAssertEqual(cells?.3, 1) - XCTAssertEqual(cells?.4, -2_147_483_647) - XCTAssertEqual(cells?.5, 2_147_483_647) - XCTAssertEqual(cells?.6, 1) - XCTAssertEqual(cells?.7, -9_223_372_036_854_775_807) - XCTAssertEqual(cells?.8, 9_223_372_036_854_775_807) - } - - func testEncodeAndDecodeIntArray() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let eventLoop = eventLoopGroup.next() - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow(try conn?.close().wait()) } - - var result: PostgresQueryResult? - let array: [Int64] = [1, 2, 3] - XCTAssertNoThrow(result = try conn?.query("SELECT \(array)::int8[] as array", logger: .psqlTest).wait()) - XCTAssertEqual(result?.rows.count, 1) - XCTAssertEqual(try result?.rows.first?.decode([Int64].self, context: .default), array) - } - - func testDecodeEmptyIntegerArray() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let eventLoop = eventLoopGroup.next() - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow(try conn?.close().wait()) } - - var result: PostgresQueryResult? - XCTAssertNoThrow(result = try conn?.query("SELECT '{}'::int[] as array", logger: .psqlTest).wait()) - - XCTAssertEqual(result?.rows.count, 1) - XCTAssertEqual(try result?.rows.first?.decode([Int64].self, context: .default), []) - } - - func testDoubleArraySerialization() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let eventLoop = eventLoopGroup.next() - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow(try conn?.close().wait()) } - - var result: PostgresQueryResult? - let doubles: [Double] = [3.14, 42] - XCTAssertNoThrow(result = try conn?.query("SELECT \(doubles)::double precision[] as doubles", logger: .psqlTest).wait()) - XCTAssertEqual(result?.rows.count, 1) - XCTAssertEqual(try result?.rows.first?.decode([Double].self, context: .default), doubles) - } - - func testDecodeDates() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let eventLoop = eventLoopGroup.next() - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow(try conn?.close().wait()) } - - var result: PostgresQueryResult? - XCTAssertNoThrow(result = try conn?.query(""" - SELECT - '2016-01-18 01:02:03 +0042'::DATE as date, - '2016-01-18 01:02:03 +0042'::TIMESTAMP as timestamp, - '2016-01-18 01:02:03 +0042'::TIMESTAMPTZ as timestamptz - """, logger: .psqlTest).wait()) - - XCTAssertEqual(result?.rows.count, 1) - - var cells: (Date, Date, Date)? - XCTAssertNoThrow(cells = try result?.rows.first?.decode((Date, Date, Date).self, context: .default)) - - XCTAssertEqual(cells?.0.description, "2016-01-18 00:00:00 +0000") - XCTAssertEqual(cells?.1.description, "2016-01-18 01:02:03 +0000") - XCTAssertEqual(cells?.2.description, "2016-01-18 00:20:03 +0000") - } - - func testDecodeDecimals() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let eventLoop = eventLoopGroup.next() - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow(try conn?.close().wait()) } - - var result: PostgresQueryResult? - XCTAssertNoThrow(result = try conn?.query(""" - SELECT - \(Decimal(string: "123456.789123")!)::numeric as numeric, - \(Decimal(string: "-123456.789123")!)::numeric as numeric_negative - """, logger: .psqlTest).wait()) - XCTAssertEqual(result?.rows.count, 1) - - var cells: (Decimal, Decimal)? - XCTAssertNoThrow(cells = try result?.rows.first?.decode((Decimal, Decimal).self, context: .default)) - - XCTAssertEqual(cells?.0, Decimal(string: "123456.789123")) - XCTAssertEqual(cells?.1, Decimal(string: "-123456.789123")) - } - - func testDecodeRawRepresentables() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let eventLoop = eventLoopGroup.next() - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow(try conn?.close().wait()) } - - enum StringRR: String, PostgresDecodable { - case a - } - - enum IntRR: Int, PostgresDecodable { - case b - } - - let stringValue = StringRR.a - let intValue = IntRR.b - - var result: PostgresQueryResult? - XCTAssertNoThrow(result = try conn?.query(""" - SELECT - \(stringValue.rawValue)::varchar as string, - \(intValue.rawValue)::int8 as int - """, logger: .psqlTest).wait()) - XCTAssertEqual(result?.rows.count, 1) - - var cells: (StringRR, IntRR)? - XCTAssertNoThrow(cells = try result?.rows.first?.decode((StringRR, IntRR).self, context: .default)) - - XCTAssertEqual(cells?.0, stringValue) - XCTAssertEqual(cells?.1, intValue) - } - - func testRoundTripUUID() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let eventLoop = eventLoopGroup.next() - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow(try conn?.close().wait()) } - - let uuidString = "2c68f645-9ca6-468b-b193-ee97f241c2f8" - - var result: PostgresQueryResult? - XCTAssertNoThrow(result = try conn?.query(""" - SELECT \(uuidString)::UUID as uuid - """, - logger: .psqlTest - ).wait()) - - XCTAssertEqual(result?.rows.count, 1) - XCTAssertEqual(try result?.rows.first?.decode(UUID.self, context: .default), UUID(uuidString: uuidString)) - } - - func testRoundTripJSONB() { - struct Object: Codable, PostgresCodable { - let foo: Int - let bar: Int - } - - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let eventLoop = eventLoopGroup.next() - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow(try conn?.close().wait()) } - - do { - var result: PostgresQueryResult? - XCTAssertNoThrow(result = try conn?.query(""" - select \(Object(foo: 1, bar: 2))::jsonb as jsonb - """, logger: .psqlTest).wait()) - - XCTAssertEqual(result?.rows.count, 1) - var obj: Object? - XCTAssertNoThrow(obj = try result?.rows.first?.decode(Object.self, context: .default)) - XCTAssertEqual(obj?.foo, 1) - XCTAssertEqual(obj?.bar, 2) - } - - do { - var result: PostgresQueryResult? - XCTAssertNoThrow(result = try conn?.query(""" - select \(Object(foo: 1, bar: 2))::json as json - """, logger: .psqlTest).wait()) - - XCTAssertEqual(result?.rows.count, 1) - var obj: Object? - XCTAssertNoThrow(obj = try result?.rows.first?.decode(Object.self, context: .default)) - XCTAssertEqual(obj?.foo, 1) - XCTAssertEqual(obj?.bar, 2) - } - } - +// func testQueryVersion() { +// let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) +// defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } +// let eventLoop = eventLoopGroup.next() +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow(try conn?.close().wait()) } +// +// var result: PostgresQueryResult? +// XCTAssertNoThrow(result = try conn?.query("SELECT version()", logger: .psqlTest).wait()) +// let rows = result?.rows +// var version: String? +// XCTAssertNoThrow(version = try rows?.first?.decode(String.self, context: .default)) +// XCTAssertEqual(version?.contains("PostgreSQL"), true) +// } +// +// func testQuery10kItems() { +// let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) +// defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } +// let eventLoop = eventLoopGroup.next() +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow(try conn?.close().wait()) } +// +// var metadata: PostgresQueryMetadata? +// let received = ManagedAtomic<Int64>(0) +// XCTAssertNoThrow(metadata = try conn?.query("SELECT generate_series(1, 10000);", logger: .psqlTest) { row in +// func workaround() { +// let expected = received.wrappingIncrementThenLoad(ordering: .relaxed) +// XCTAssertEqual(expected, try row.decode(Int64.self, context: .default)) +// } +// +// workaround() +// }.wait()) +// +// XCTAssertEqual(received.load(ordering: .relaxed), 10000) +// XCTAssertEqual(metadata?.command, "SELECT") +// XCTAssertEqual(metadata?.rows, 10000) +// } +// +// func test1kRoundTrips() { +// let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) +// defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } +// let eventLoop = eventLoopGroup.next() +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow(try conn?.close().wait()) } +// +// for _ in 0..<1_000 { +// var result: PostgresQueryResult? +// XCTAssertNoThrow(result = try conn?.query("SELECT version()", logger: .psqlTest).wait()) +// var version: String? +// XCTAssertNoThrow(version = try result?.rows.first?.decode(String.self, context: .default)) +// XCTAssertEqual(version?.contains("PostgreSQL"), true) +// } +// } +// +// func testQuerySelectParameter() { +// let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) +// defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } +// let eventLoop = eventLoopGroup.next() +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow(try conn?.close().wait()) } +// +// var result: PostgresQueryResult? +// XCTAssertNoThrow(result = try conn?.query("SELECT \("hello")::TEXT as foo", logger: .psqlTest).wait()) +// var foo: String? +// XCTAssertNoThrow(foo = try result?.rows.first?.decode(String.self, context: .default)) +// XCTAssertEqual(foo, "hello") +// } +// +// func testQueryNothing() throws { +// let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) +// defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } +// let eventLoop = eventLoopGroup.next() +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow(try conn?.close().wait()) } +// +// var _result: PostgresQueryResult? +// XCTAssertNoThrow(_result = try conn?.query(""" +// -- Some comments +// """, logger: .psqlTest).wait()) +// +// let result = try XCTUnwrap(_result) +// XCTAssertEqual(result.rows, []) +// XCTAssertEqual(result.metadata.command, "") +// } +// +// func testDecodeIntegers() { +// let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) +// defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } +// let eventLoop = eventLoopGroup.next() +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow(try conn?.close().wait()) } +// +// var result: PostgresQueryResult? +// XCTAssertNoThrow(result = try conn?.query(""" +// SELECT +// 1::SMALLINT as smallint, +// -32767::SMALLINT as smallint_min, +// 32767::SMALLINT as smallint_max, +// 1::INT as int, +// -2147483647::INT as int_min, +// 2147483647::INT as int_max, +// 1::BIGINT as bigint, +// -9223372036854775807::BIGINT as bigint_min, +// 9223372036854775807::BIGINT as bigint_max +// """, logger: .psqlTest).wait()) +// +// XCTAssertEqual(result?.rows.count, 1) +// let row = result?.rows.first +// +// var cells: (Int16, Int16, Int16, Int32, Int32, Int32, Int64, Int64, Int64)? +// XCTAssertNoThrow(cells = try row?.decode((Int16, Int16, Int16, Int32, Int32, Int32, Int64, Int64, Int64).self, context: .default)) +// +// XCTAssertEqual(cells?.0, 1) +// XCTAssertEqual(cells?.1, -32_767) +// XCTAssertEqual(cells?.2, 32_767) +// XCTAssertEqual(cells?.3, 1) +// XCTAssertEqual(cells?.4, -2_147_483_647) +// XCTAssertEqual(cells?.5, 2_147_483_647) +// XCTAssertEqual(cells?.6, 1) +// XCTAssertEqual(cells?.7, -9_223_372_036_854_775_807) +// XCTAssertEqual(cells?.8, 9_223_372_036_854_775_807) +// } +// +// func testEncodeAndDecodeIntArray() { +// let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) +// defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } +// let eventLoop = eventLoopGroup.next() +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow(try conn?.close().wait()) } +// +// var result: PostgresQueryResult? +// let array: [Int64] = [1, 2, 3] +// XCTAssertNoThrow(result = try conn?.query("SELECT \(array)::int8[] as array", logger: .psqlTest).wait()) +// XCTAssertEqual(result?.rows.count, 1) +// XCTAssertEqual(try result?.rows.first?.decode([Int64].self, context: .default), array) +// } +// +// func testDecodeEmptyIntegerArray() { +// let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) +// defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } +// let eventLoop = eventLoopGroup.next() +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow(try conn?.close().wait()) } +// +// var result: PostgresQueryResult? +// XCTAssertNoThrow(result = try conn?.query("SELECT '{}'::int[] as array", logger: .psqlTest).wait()) +// +// XCTAssertEqual(result?.rows.count, 1) +// XCTAssertEqual(try result?.rows.first?.decode([Int64].self, context: .default), []) +// } +// +// func testDoubleArraySerialization() { +// let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) +// defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } +// let eventLoop = eventLoopGroup.next() +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow(try conn?.close().wait()) } +// +// var result: PostgresQueryResult? +// let doubles: [Double] = [3.14, 42] +// XCTAssertNoThrow(result = try conn?.query("SELECT \(doubles)::double precision[] as doubles", logger: .psqlTest).wait()) +// XCTAssertEqual(result?.rows.count, 1) +// XCTAssertEqual(try result?.rows.first?.decode([Double].self, context: .default), doubles) +// } +// +// func testDecodeDates() { +// let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) +// defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } +// let eventLoop = eventLoopGroup.next() +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow(try conn?.close().wait()) } +// +// var result: PostgresQueryResult? +// XCTAssertNoThrow(result = try conn?.query(""" +// SELECT +// '2016-01-18 01:02:03 +0042'::DATE as date, +// '2016-01-18 01:02:03 +0042'::TIMESTAMP as timestamp, +// '2016-01-18 01:02:03 +0042'::TIMESTAMPTZ as timestamptz +// """, logger: .psqlTest).wait()) +// +// XCTAssertEqual(result?.rows.count, 1) +// +// var cells: (Date, Date, Date)? +// XCTAssertNoThrow(cells = try result?.rows.first?.decode((Date, Date, Date).self, context: .default)) +// +// XCTAssertEqual(cells?.0.description, "2016-01-18 00:00:00 +0000") +// XCTAssertEqual(cells?.1.description, "2016-01-18 01:02:03 +0000") +// XCTAssertEqual(cells?.2.description, "2016-01-18 00:20:03 +0000") +// } +// +// func testDecodeDecimals() { +// let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) +// defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } +// let eventLoop = eventLoopGroup.next() +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow(try conn?.close().wait()) } +// +// var result: PostgresQueryResult? +// XCTAssertNoThrow(result = try conn?.query(""" +// SELECT +// \(Decimal(string: "123456.789123")!)::numeric as numeric, +// \(Decimal(string: "-123456.789123")!)::numeric as numeric_negative +// """, logger: .psqlTest).wait()) +// XCTAssertEqual(result?.rows.count, 1) +// +// var cells: (Decimal, Decimal)? +// XCTAssertNoThrow(cells = try result?.rows.first?.decode((Decimal, Decimal).self, context: .default)) +// +// XCTAssertEqual(cells?.0, Decimal(string: "123456.789123")) +// XCTAssertEqual(cells?.1, Decimal(string: "-123456.789123")) +// } +// +// func testDecodeRawRepresentables() { +// let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) +// defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } +// let eventLoop = eventLoopGroup.next() +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow(try conn?.close().wait()) } +// +// enum StringRR: String, PostgresDecodable { +// case a +// } +// +// enum IntRR: Int, PostgresDecodable { +// case b +// } +// +// let stringValue = StringRR.a +// let intValue = IntRR.b +// +// var result: PostgresQueryResult? +// XCTAssertNoThrow(result = try conn?.query(""" +// SELECT +// \(stringValue.rawValue)::varchar as string, +// \(intValue.rawValue)::int8 as int +// """, logger: .psqlTest).wait()) +// XCTAssertEqual(result?.rows.count, 1) +// +// var cells: (StringRR, IntRR)? +// XCTAssertNoThrow(cells = try result?.rows.first?.decode((StringRR, IntRR).self, context: .default)) +// +// XCTAssertEqual(cells?.0, stringValue) +// XCTAssertEqual(cells?.1, intValue) +// } +// +// func testRoundTripUUID() { +// let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) +// defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } +// let eventLoop = eventLoopGroup.next() +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow(try conn?.close().wait()) } +// +// let uuidString = "2c68f645-9ca6-468b-b193-ee97f241c2f8" +// +// var result: PostgresQueryResult? +// XCTAssertNoThrow(result = try conn?.query(""" +// SELECT \(uuidString)::UUID as uuid +// """, +// logger: .psqlTest +// ).wait()) +// +// XCTAssertEqual(result?.rows.count, 1) +// XCTAssertEqual(try result?.rows.first?.decode(UUID.self, context: .default), UUID(uuidString: uuidString)) +// } +// +// func testRoundTripJSONB() { +// struct Object: Codable, PostgresCodable { +// let foo: Int +// let bar: Int +// } +// +// let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) +// defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } +// let eventLoop = eventLoopGroup.next() +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow(try conn?.close().wait()) } +// +// do { +// var result: PostgresQueryResult? +// XCTAssertNoThrow(result = try conn?.query(""" +// select \(Object(foo: 1, bar: 2))::jsonb as jsonb +// """, logger: .psqlTest).wait()) +// +// XCTAssertEqual(result?.rows.count, 1) +// var obj: Object? +// XCTAssertNoThrow(obj = try result?.rows.first?.decode(Object.self, context: .default)) +// XCTAssertEqual(obj?.foo, 1) +// XCTAssertEqual(obj?.bar, 2) +// } +// +// do { +// var result: PostgresQueryResult? +// XCTAssertNoThrow(result = try conn?.query(""" +// select \(Object(foo: 1, bar: 2))::json as json +// """, logger: .psqlTest).wait()) +// +// XCTAssertEqual(result?.rows.count, 1) +// var obj: Object? +// XCTAssertNoThrow(obj = try result?.rows.first?.decode(Object.self, context: .default)) +// XCTAssertEqual(obj?.foo, 1) +// XCTAssertEqual(obj?.bar, 2) +// } +// } } diff --git a/Tests/IntegrationTests/PerformanceTests.swift b/Tests/IntegrationTests/PerformanceTests.swift deleted file mode 100644 index 6f730560..00000000 --- a/Tests/IntegrationTests/PerformanceTests.swift +++ /dev/null @@ -1,310 +0,0 @@ -import XCTest -import Logging -import NIOCore -import NIOPosix -@testable import PostgresNIO -import NIOTestUtils - -final class PerformanceTests: XCTestCase { - private var group: EventLoopGroup! - - private var eventLoop: EventLoop { self.group.next() } - - override func setUpWithError() throws { - try super.setUpWithError() - try XCTSkipUnless(Self.shouldRunPerformanceTests) - - XCTAssertTrue(isLoggingConfigured) - self.group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - } - - override func tearDownWithError() throws { - try self.group?.syncShutdownGracefully() - self.group = nil - try super.tearDownWithError() - } - - - // MARK: Performance - - func testPerformanceRangeSelectDecodePerformance() throws { - struct Series: Decodable { - var num: Int - } - - let conn = try PostgresConnection.test(on: eventLoop).wait() - defer { try! conn.close().wait() } - measure { - do { - for _ in 0..<5 { - try conn.query("SELECT * FROM generate_series(1, 10000) num") { row in - _ = try row.decode(Int.self, context: .default) - }.wait() - } - } catch { - XCTFail("\(error)") - } - } - } - - func testPerformanceSelectTinyModel() throws { - let conn = try PostgresConnection.test(on: eventLoop).wait() - defer { try! conn.close().wait() } - - try prepareTableToMeasureSelectPerformance( - rowCount: 300_000, batchSize: 5_000, - schema: - """ - "int" int8, - """, - fixtureData: [PostgresData(int: 1234)], - on: self.eventLoop - ) - defer { _ = try! conn.simpleQuery("DROP TABLE \"measureSelectPerformance\"").wait() } - - measure { - do { - try conn.query("SELECT * FROM \"measureSelectPerformance\"") { row in - _ = try row.decode(Int.self, context: .default) - }.wait() - } catch { - XCTFail("\(error)") - } - } - } - - @available(*, deprecated, message: "Testing deprecated functionality") - func testPerformanceSelectMediumModel() throws { - let conn = try PostgresConnection.test(on: eventLoop).wait() - defer { try! conn.close().wait() } - - let now = Date() - let uuid = UUID() - try prepareTableToMeasureSelectPerformance( - rowCount: 300_000, - schema: - // TODO: Also add a `Double` and a `Data` field to this performance test. - """ - "string" text, - "int" int8, - "date" timestamptz, - "uuid" uuid, - """, - fixtureData: [ - PostgresData(string: "foo"), - PostgresData(int: 0), - now.postgresData!, - PostgresData(uuid: uuid) - ], - on: self.eventLoop - ) - defer { _ = try! conn.simpleQuery("DROP TABLE \"measureSelectPerformance\"").wait() } - - measure { - do { - try conn.query("SELECT * FROM \"measureSelectPerformance\"") { - let row = $0.makeRandomAccess() - _ = row[data: "id"].int - _ = row[data: "string"].string - _ = row[data: "int"].int - _ = row[data: "date"].date - _ = row[data: "uuid"].uuid - }.wait() - } catch { - XCTFail("\(error)") - } - } - } - - @available(*, deprecated, message: "Testing deprecated functionality") - func testPerformanceSelectLargeModel() throws { - let conn = try PostgresConnection.test(on: eventLoop).wait() - defer { try! conn.close().wait() } - - let now = Date() - let uuid = UUID() - try prepareTableToMeasureSelectPerformance( - rowCount: 100_000, - schema: - // TODO: Also add `Double` and `Data` fields to this performance test. - """ - "string1" text, - "string2" text, - "string3" text, - "string4" text, - "string5" text, - "int1" int8, - "int2" int8, - "int3" int8, - "int4" int8, - "int5" int8, - "date1" timestamptz, - "date2" timestamptz, - "date3" timestamptz, - "date4" timestamptz, - "date5" timestamptz, - "uuid1" uuid, - "uuid2" uuid, - "uuid3" uuid, - "uuid4" uuid, - "uuid5" uuid, - """, - fixtureData: [ - PostgresData(string: "string1"), - PostgresData(string: "string2"), - PostgresData(string: "string3"), - PostgresData(string: "string4"), - PostgresData(string: "string5"), - PostgresData(int: 1), - PostgresData(int: 2), - PostgresData(int: 3), - PostgresData(int: 4), - PostgresData(int: 5), - now.postgresData!, - now.postgresData!, - now.postgresData!, - now.postgresData!, - now.postgresData!, - PostgresData(uuid: uuid), - PostgresData(uuid: uuid), - PostgresData(uuid: uuid), - PostgresData(uuid: uuid), - PostgresData(uuid: uuid) - ], - on: self.eventLoop - ) - defer { _ = try! conn.simpleQuery("DROP TABLE \"measureSelectPerformance\"").wait() } - - measure { - do { - try conn.query("SELECT * FROM \"measureSelectPerformance\"") { - let row = $0.makeRandomAccess() - _ = row[data: "id"].int - _ = row[data: "string1"].string - _ = row[data: "string2"].string - _ = row[data: "string3"].string - _ = row[data: "string4"].string - _ = row[data: "string5"].string - _ = row[data: "int1"].int - _ = row[data: "int2"].int - _ = row[data: "int3"].int - _ = row[data: "int4"].int - _ = row[data: "int5"].int - _ = row[data: "date1"].date - _ = row[data: "date2"].date - _ = row[data: "date3"].date - _ = row[data: "date4"].date - _ = row[data: "date5"].date - _ = row[data: "uuid1"].uuid - _ = row[data: "uuid2"].uuid - _ = row[data: "uuid3"].uuid - _ = row[data: "uuid4"].uuid - _ = row[data: "uuid5"].uuid - }.wait() - } catch { - XCTFail("\(error)") - } - } - } - - func testPerformanceSelectLargeModelWithLongFieldNames() throws { - let conn = try PostgresConnection.test(on: eventLoop).wait() - defer { try! conn.close().wait() } - - let fieldIndices = Array(1...20) - let fieldNames = fieldIndices.map { "veryLongFieldNameVeryLongFieldName\($0)" } - try prepareTableToMeasureSelectPerformance( - rowCount: 50_000, batchSize: 200, - schema: fieldNames.map { "\"\($0)\" int8" }.joined(separator: ", ") + ",", - fixtureData: fieldIndices.map { PostgresData(int: $0) }, - on: self.eventLoop - ) - defer { _ = try! conn.simpleQuery("DROP TABLE \"measureSelectPerformance\"").wait() } - - measure { - do { - try conn.query("SELECT * FROM \"measureSelectPerformance\"") { - let row = $0.makeRandomAccess() - _ = row[data: "id"].int - for fieldName in fieldNames { - _ = row[data: fieldName].int - } - }.wait() - } catch { - XCTFail("\(error)") - } - } - } - - func testPerformanceSelectHugeModel() throws { - let conn = try PostgresConnection.test(on: eventLoop).wait() - defer { try! conn.close().wait() } - - let fieldIndices = Array(1...100) - let fieldNames = fieldIndices.map { "int\($0)" } - try prepareTableToMeasureSelectPerformance( - rowCount: 10_000, batchSize: 200, - schema: fieldNames.map { "\"\($0)\" int8" }.joined(separator: ", ") + ",", - fixtureData: fieldIndices.map { PostgresData(int: $0) }, - on: self.eventLoop - ) - defer { _ = try! conn.simpleQuery("DROP TABLE \"measureSelectPerformance\"").wait() } - - measure { - do { - try conn.query("SELECT * FROM \"measureSelectPerformance\"") { - let row = $0.makeRandomAccess() - _ = row[data: "id"].int - for fieldName in fieldNames { - _ = row[data: fieldName].int - } - }.wait() - } catch { - XCTFail("\(error)") - } - } - } - -} - -private func prepareTableToMeasureSelectPerformance( - rowCount: Int, - batchSize: Int = 1_000, - schema: String, - fixtureData: [PostgresData], - on eventLoop: EventLoop, - file: StaticString = #filePath, - line: UInt = #line -) throws { - XCTAssertEqual(rowCount % batchSize, 0, "`rowCount` must be a multiple of `batchSize`", file: (file), line: line) - let conn = try PostgresConnection.test(on: eventLoop).wait() - defer { try! conn.close().wait() } - - _ = try conn.simpleQuery("DROP TABLE IF EXISTS \"measureSelectPerformance\"").wait() - _ = try conn.simpleQuery(""" - CREATE TABLE "measureSelectPerformance" ( - "id" int8 NOT NULL, - \(schema) - PRIMARY KEY ("id") - ); - """).wait() - - // Batch `batchSize` inserts into one for better insert performance. - let totalArgumentsPerRow = fixtureData.count + 1 - let insertArgumentsPlaceholder = (0..<batchSize).map { indexInBatch in - "(" - + (0..<totalArgumentsPerRow).map { argumentIndex in "$\(indexInBatch * totalArgumentsPerRow + argumentIndex + 1)" } - .joined(separator: ", ") - + ")" - }.joined(separator: ", ") - let insertQuery = "INSERT INTO \"measureSelectPerformance\" VALUES \(insertArgumentsPlaceholder)" - var batchedFixtureData = Array(repeating: [PostgresData(int: 0)] + fixtureData, count: batchSize).flatMap { $0 } - for batchIndex in 0..<(rowCount / batchSize) { - for indexInBatch in 0..<batchSize { - let rowIndex = batchIndex * batchSize + indexInBatch - batchedFixtureData[indexInBatch * totalArgumentsPerRow] = PostgresData(int: rowIndex) - } - _ = try conn.query(insertQuery, batchedFixtureData).wait() - } -} - diff --git a/Tests/IntegrationTests/PostgresNIOTests.swift b/Tests/IntegrationTests/PostgresNIOTests.swift index ff59209b..67d20c86 100644 --- a/Tests/IntegrationTests/PostgresNIOTests.swift +++ b/Tests/IntegrationTests/PostgresNIOTests.swift @@ -47,1436 +47,2723 @@ final class PostgresNIOTests: XCTestCase { try conn.close().wait() } - func testSimpleQueryVersion() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: [PostgresRow]? - XCTAssertNoThrow(rows = try conn?.simpleQuery("SELECT version()").wait()) - XCTAssertEqual(rows?.count, 1) - XCTAssertEqual(try rows?.first?.decode(String.self, context: .default).contains("PostgreSQL"), true) - } - - func testSimpleQueryVersionUsingUDS() throws { - try XCTSkipUnless(env("POSTGRES_SOCKET") != nil) - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.testUDS(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: [PostgresRow]? - XCTAssertNoThrow(rows = try conn?.simpleQuery("SELECT version()").wait()) - XCTAssertEqual(rows?.count, 1) - XCTAssertEqual(try rows?.first?.decode(String.self, context: .default).contains("PostgreSQL"), true) - } - - func testSimpleQueryVersionUsingEstablishedChannel() throws { - let channel = try ClientBootstrap(group: self.group).connect(to: PostgresConnection.address()).wait() - let conn = try PostgresConnection.testChannel(channel, on: self.eventLoop).wait() - defer { XCTAssertNoThrow(try conn.close().wait()) } - - let rows = try conn.simpleQuery("SELECT version()").wait() - XCTAssertEqual(rows.count, 1) - XCTAssertEqual(try rows.first?.decode(String.self, context: .default).contains("PostgreSQL"), true) - } - - func testQueryVersion() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query("SELECT version()", .init()).wait()) - XCTAssertEqual(rows?.count, 1) - XCTAssertEqual(try rows?.first?.decode(String.self, context: .default).contains("PostgreSQL"), true) - } - - func testQuerySelectParameter() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query("SELECT $1::TEXT as foo", ["hello"]).wait()) - XCTAssertEqual(rows?.count, 1) - XCTAssertEqual(try rows?.first?.decode(String.self, context: .default), "hello") - } - - func testSQLError() throws { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - - XCTAssertThrowsError(_ = try conn?.simpleQuery("SELECT &").wait()) { error in - XCTAssertEqual((error as? PostgresError)?.code, .syntaxError) - } - } - - func testNotificationsEmptyPayload() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - - let receivedNotifications = ManagedAtomic<Int>(0) - conn?.addListener(channel: "example") { context, notification in - receivedNotifications.wrappingIncrement(ordering: .relaxed) - XCTAssertEqual(notification.channel, "example") - XCTAssertEqual(notification.payload, "") - } - XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) - // Notifications are asynchronous, so we should run at least one more query to make sure we'll have received the notification response by then - XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) - XCTAssertEqual(receivedNotifications.load(ordering: .relaxed), 1) - } - - func testNotificationsNonEmptyPayload() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - let receivedNotifications = ManagedAtomic<Int>(0) - conn?.addListener(channel: "example") { context, notification in - receivedNotifications.wrappingIncrement(ordering: .relaxed) - XCTAssertEqual(notification.channel, "example") - XCTAssertEqual(notification.payload, "Notification payload example") - } - XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example, 'Notification payload example'").wait()) - // Notifications are asynchronous, so we should run at least one more query to make sure we'll have received the notification response by then - XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) - XCTAssertEqual(receivedNotifications.load(ordering: .relaxed), 1) - } - - func testNotificationsRemoveHandlerWithinHandler() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - let receivedNotifications = ManagedAtomic<Int>(0) - conn?.addListener(channel: "example") { context, notification in - receivedNotifications.wrappingIncrement(ordering: .relaxed) - context.stop() - } - XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) - XCTAssertEqual(receivedNotifications.load(ordering: .relaxed), 1) - } - - func testNotificationsRemoveHandlerOutsideHandler() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - let receivedNotifications = ManagedAtomic<Int>(0) - let context = conn?.addListener(channel: "example") { context, notification in - receivedNotifications.wrappingIncrement(ordering: .relaxed) - } - XCTAssertNotNil(context) - XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) - context?.stop() - XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) - XCTAssertEqual(receivedNotifications.load(ordering: .relaxed), 1) - } - - func testNotificationsMultipleRegisteredHandlers() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - let receivedNotifications1 = ManagedAtomic<Int>(0) - conn?.addListener(channel: "example") { context, notification in - receivedNotifications1.wrappingIncrement(ordering: .relaxed) - } - let receivedNotifications2 = ManagedAtomic<Int>(0) - conn?.addListener(channel: "example") { context, notification in - receivedNotifications2.wrappingIncrement(ordering: .relaxed) - } - XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) - XCTAssertEqual(receivedNotifications1.load(ordering: .relaxed), 1) - XCTAssertEqual(receivedNotifications2.load(ordering: .relaxed), 1) - } - - func testNotificationsMultipleRegisteredHandlersRemoval() throws { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - let receivedNotifications1 = ManagedAtomic<Int>(0) - XCTAssertNotNil(conn?.addListener(channel: "example") { context, notification in - receivedNotifications1.wrappingIncrement(ordering: .relaxed) - context.stop() - }) - let receivedNotifications2 = ManagedAtomic<Int>(0) - XCTAssertNotNil(conn?.addListener(channel: "example") { context, notification in - receivedNotifications2.wrappingIncrement(ordering: .relaxed) - }) - XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) - XCTAssertEqual(receivedNotifications1.load(ordering: .relaxed), 1) - XCTAssertEqual(receivedNotifications2.load(ordering: .relaxed), 2) - } - - func testNotificationHandlerFiltersOnChannel() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - XCTAssertNotNil(conn?.addListener(channel: "desired") { context, notification in - XCTFail("Received notification on channel that handler was not registered for") - }) - XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN undesired").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY undesired").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) - } - - func testSelectTypes() throws { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var results: [PostgresRow]? - XCTAssertNoThrow(results = try conn?.simpleQuery("SELECT * FROM pg_type").wait()) - XCTAssert((results?.count ?? 0) > 350, "Results count not large enough") - } - - func testSelectType() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var results: [PostgresRow]? - XCTAssertNoThrow(results = try conn?.simpleQuery("SELECT * FROM pg_type WHERE typname = 'float8'").wait()) - // [ - // "typreceive": "float8recv", - // "typelem": "0", - // "typarray": "1022", - // "typalign": "d", - // "typanalyze": "-", - // "typtypmod": "-1", - // "typname": "float8", - // "typnamespace": "11", - // "typdefault": "<null>", - // "typdefaultbin": "<null>", - // "typcollation": "0", - // "typispreferred": "t", - // "typrelid": "0", - // "typbyval": "t", - // "typnotnull": "f", - // "typinput": "float8in", - // "typlen": "8", - // "typcategory": "N", - // "typowner": "10", - // "typtype": "b", - // "typdelim": ",", - // "typndims": "0", - // "typbasetype": "0", - // "typacl": "<null>", - // "typisdefined": "t", - // "typmodout": "-", - // "typmodin": "-", - // "typsend": "float8send", - // "typstorage": "p", - // "typoutput": "float8out" - // ] - XCTAssertEqual(results?.count, 1) - let row = results?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "typname"].string, "float8") - XCTAssertEqual(row?[data: "typnamespace"].int, 11) - XCTAssertEqual(row?[data: "typowner"].int, 10) - XCTAssertEqual(row?[data: "typlen"].int, 8) - } - - func testIntegers() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - struct Integers: Decodable { - let smallint: Int16 - let smallint_min: Int16 - let smallint_max: Int16 - let int: Int32 - let int_min: Int32 - let int_max: Int32 - let bigint: Int64 - let bigint_min: Int64 - let bigint_max: Int64 - } - var results: PostgresQueryResult? - XCTAssertNoThrow(results = try conn?.query(""" - SELECT - 1::SMALLINT as smallint, - -32767::SMALLINT as smallint_min, - 32767::SMALLINT as smallint_max, - 1::INT as int, - -2147483647::INT as int_min, - 2147483647::INT as int_max, - 1::BIGINT as bigint, - -9223372036854775807::BIGINT as bigint_min, - 9223372036854775807::BIGINT as bigint_max - """).wait()) - XCTAssertEqual(results?.count, 1) - - let row = results?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "smallint"].int16, 1) - XCTAssertEqual(row?[data: "smallint_min"].int16, -32_767) - XCTAssertEqual(row?[data: "smallint_max"].int16, 32_767) - XCTAssertEqual(row?[data: "int"].int32, 1) - XCTAssertEqual(row?[data: "int_min"].int32, -2_147_483_647) - XCTAssertEqual(row?[data: "int_max"].int32, 2_147_483_647) - XCTAssertEqual(row?[data: "bigint"].int64, 1) - XCTAssertEqual(row?[data: "bigint_min"].int64, -9_223_372_036_854_775_807) - XCTAssertEqual(row?[data: "bigint_max"].int64, 9_223_372_036_854_775_807) - } - - func testPi() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - - struct Pi: Decodable { - let text: String - let numeric_string: String - let numeric_decimal: Decimal - let double: Double - let float: Float - } - var results: PostgresQueryResult? - XCTAssertNoThrow(results = try conn?.query(""" - SELECT - pi()::TEXT as text, - pi()::NUMERIC as numeric_string, - pi()::NUMERIC as numeric_decimal, - pi()::FLOAT8 as double, - pi()::FLOAT4 as float - """).wait()) - XCTAssertEqual(results?.count, 1) - let row = results?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "text"].string?.hasPrefix("3.14159265"), true) - XCTAssertEqual(row?[data: "numeric_string"].string?.hasPrefix("3.14159265"), true) - XCTAssertTrue(row?[data: "numeric_decimal"].decimal?.isLess(than: 3.14159265358980) ?? false) - XCTAssertFalse(row?[data: "numeric_decimal"].decimal?.isLess(than: 3.14159265358978) ?? true) - XCTAssertTrue(row?[data: "double"].double?.description.hasPrefix("3.141592") ?? false) - XCTAssertTrue(row?[data: "float"].float?.description.hasPrefix("3.141592") ?? false) - } - - func testUUID() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - struct Model: Decodable { - let id: UUID - let string: String - } - var results: PostgresQueryResult? - XCTAssertNoThrow(results = try conn?.query(""" - SELECT - '123e4567-e89b-12d3-a456-426655440000'::UUID as id, - '123e4567-e89b-12d3-a456-426655440000'::UUID as string - """).wait()) - XCTAssertEqual(results?.count, 1) - let row = results?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "id"].uuid, UUID(uuidString: "123E4567-E89B-12D3-A456-426655440000")) - XCTAssertEqual(UUID(uuidString: row?[data: "id"].string ?? ""), UUID(uuidString: "123E4567-E89B-12D3-A456-426655440000")) - } - - func testInt4Range() async throws { - let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get() - self.addTeardownBlock { - try await conn.close() - } - struct Model: Decodable { - let range: Range<Int32> - } - let results1: PostgresQueryResult = try await conn.query(""" - SELECT - '[\(Int32.min), \(Int32.max))'::int4range AS range - """).get() - XCTAssertEqual(results1.count, 1) - var row = results1.first?.makeRandomAccess() - let expectedRange: Range<Int32> = Int32.min..<Int32.max - let decodedRange = try row?.decode(column: "range", as: Range<Int32>.self, context: .default) - XCTAssertEqual(decodedRange, expectedRange) - - let results2 = try await conn.query(""" - SELECT - ARRAY[ - '[0, 1)'::int4range, - '[10, 11)'::int4range - ] AS ranges - """).get() - XCTAssertEqual(results2.count, 1) - row = results2.first?.makeRandomAccess() - let decodedRangeArray = try row?.decode(column: "ranges", as: [Range<Int32>].self, context: .default) - let decodedClosedRangeArray = try row?.decode(column: "ranges", as: [ClosedRange<Int32>].self, context: .default) - XCTAssertEqual(decodedRangeArray, [0..<1, 10..<11]) - XCTAssertEqual(decodedClosedRangeArray, [0...0, 10...10]) - } - - func testEmptyInt4Range() async throws { - let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get() - self.addTeardownBlock { - try await conn.close() - } - struct Model: Decodable { - let range: Range<Int32> - } - let randomValue = Int32.random(in: Int32.min...Int32.max) - let results: PostgresQueryResult = try await conn.query(""" - SELECT - '[\(randomValue),\(randomValue))'::int4range AS range - """).get() - XCTAssertEqual(results.count, 1) - let row = results.first?.makeRandomAccess() - let expectedRange: Range<Int32> = Int32.valueForEmptyRange..<Int32.valueForEmptyRange - let decodedRange = try row?.decode(column: "range", as: Range<Int32>.self, context: .default) - XCTAssertEqual(decodedRange, expectedRange) - - XCTAssertThrowsError( - try row?.decode(column: "range", as: ClosedRange<Int32>.self, context: .default) - ) - } - - func testInt8Range() async throws { - let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get() - self.addTeardownBlock { - try await conn.close() - } - struct Model: Decodable { - let range: Range<Int64> - } - let results1: PostgresQueryResult = try await conn.query(""" - SELECT - '[\(Int64.min), \(Int64.max))'::int8range AS range - """).get() - XCTAssertEqual(results1.count, 1) - var row = results1.first?.makeRandomAccess() - let expectedRange: Range<Int64> = Int64.min..<Int64.max - let decodedRange = try row?.decode(column: "range", as: Range<Int64>.self, context: .default) - XCTAssertEqual(decodedRange, expectedRange) - - let results2: PostgresQueryResult = try await conn.query(""" - SELECT - ARRAY[ - '[0, 1)'::int8range, - '[10, 11)'::int8range - ] AS ranges - """).get() - XCTAssertEqual(results2.count, 1) - row = results2.first?.makeRandomAccess() - let decodedRangeArray = try row?.decode(column: "ranges", as: [Range<Int64>].self, context: .default) - let decodedClosedRangeArray = try row?.decode(column: "ranges", as: [ClosedRange<Int64>].self, context: .default) - XCTAssertEqual(decodedRangeArray, [0..<1, 10..<11]) - XCTAssertEqual(decodedClosedRangeArray, [0...0, 10...10]) - } - - func testEmptyInt8Range() async throws { - let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get() - self.addTeardownBlock { - try await conn.close() - } - struct Model: Decodable { - let range: Range<Int64> - } - let randomValue = Int64.random(in: Int64.min...Int64.max) - let results: PostgresQueryResult = try await conn.query(""" - SELECT - '[\(randomValue),\(randomValue))'::int8range AS range - """).get() - XCTAssertEqual(results.count, 1) - let row = results.first?.makeRandomAccess() - let expectedRange: Range<Int64> = Int64.valueForEmptyRange..<Int64.valueForEmptyRange - let decodedRange = try row?.decode(column: "range", as: Range<Int64>.self, context: .default) - XCTAssertEqual(decodedRange, expectedRange) - - XCTAssertThrowsError( - try row?.decode(column: "range", as: ClosedRange<Int64>.self, context: .default) - ) - } - - func testDates() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - struct Dates: Decodable { - var date: Date - var timestamp: Date - var timestamptz: Date - } - var results: PostgresQueryResult? - XCTAssertNoThrow(results = try conn?.query(""" - SELECT - '2016-01-18 01:02:03 +0042'::DATE as date, - '2016-01-18 01:02:03 +0042'::TIMESTAMP as timestamp, - '2016-01-18 01:02:03 +0042'::TIMESTAMPTZ as timestamptz - """).wait()) - XCTAssertEqual(results?.count, 1) - let row = results?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "date"].date?.description, "2016-01-18 00:00:00 +0000") - XCTAssertEqual(row?[data: "timestamp"].date?.description, "2016-01-18 01:02:03 +0000") - XCTAssertEqual(row?[data: "timestamptz"].date?.description, "2016-01-18 00:20:03 +0000") - } - - /// https://github.com/vapor/nio-postgres/issues/20 - func testBindInteger() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - XCTAssertNoThrow(_ = try conn?.simpleQuery("drop table if exists person;").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("create table person(id serial primary key, first_name text, last_name text);").wait()) - defer { XCTAssertNoThrow(_ = try conn?.simpleQuery("drop table person;").wait()) } - let id = PostgresData(int32: 5) - XCTAssertNoThrow(_ = try conn?.query("SELECT id, first_name, last_name FROM person WHERE id = $1", [id]).wait()) - } - - // https://github.com/vapor/nio-postgres/issues/21 - func testAverageLengthNumeric() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var results: PostgresQueryResult? - XCTAssertNoThrow(results = try conn?.query("select avg(length('foo')) as average_length").wait()) - let row = results?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: 0].double, 3.0) - } - - func testNumericParsing() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - select - '1234.5678'::numeric as a, - '-123.456'::numeric as b, - '123456.789123'::numeric as c, - '3.14159265358979'::numeric as d, - '10000'::numeric as e, - '0.00001'::numeric as f, - '100000000'::numeric as g, - '0.000000001'::numeric as h, - '100000000000'::numeric as i, - '0.000000000001'::numeric as j, - '123000000000'::numeric as k, - '0.000000000123'::numeric as l, - '0.5'::numeric as m - """).wait()) - XCTAssertEqual(rows?.count, 1) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "a"].string, "1234.5678") - XCTAssertEqual(row?[data: "b"].string, "-123.456") - XCTAssertEqual(row?[data: "c"].string, "123456.789123") - XCTAssertEqual(row?[data: "d"].string, "3.14159265358979") - XCTAssertEqual(row?[data: "e"].string, "10000") - XCTAssertEqual(row?[data: "f"].string, "0.00001") - XCTAssertEqual(row?[data: "g"].string, "100000000") - XCTAssertEqual(row?[data: "h"].string, "0.000000001") - XCTAssertEqual(row?[data: "k"].string, "123000000000") - XCTAssertEqual(row?[data: "l"].string, "0.000000000123") - XCTAssertEqual(row?[data: "m"].string, "0.5") - } - - func testSingleNumericParsing() { - // this seemingly duped test is useful for debugging numeric parsing - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - let numeric = "790226039477542363.6032384900176272473" - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - select - '\(numeric)'::numeric as n - """).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "n"].string, numeric) - } - - func testRandomlyGeneratedNumericParsing() throws { - // this test takes a long time to run - try XCTSkipUnless(Self.shouldRunLongRunningTests) - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - - for _ in 0..<1_000_000 { - let integer = UInt.random(in: UInt.min..<UInt.max) - let fraction = UInt.random(in: UInt.min..<UInt.max) - let number = "\(integer).\(fraction)" - .trimmingCharacters(in: CharacterSet(["0"])) - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - select - '\(number)'::numeric as n - """).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "n"].string, number) - } - } - - func testNumericSerialization() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - let a = PostgresNumeric(string: "123456.789123")! - let b = PostgresNumeric(string: "-123456.789123")! - let c = PostgresNumeric(string: "3.14159265358979")! - let d = PostgresNumeric(string: "1234567898765")! - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - select - $1::numeric as a, - $2::numeric as b, - $3::numeric as c, - $4::numeric as d - """, [ - .init(numeric: a), - .init(numeric: b), - .init(numeric: c), - .init(numeric: d) - ]).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "a"].decimal, Decimal(string: "123456.789123")!) - XCTAssertEqual(row?[data: "b"].decimal, Decimal(string: "-123456.789123")!) - XCTAssertEqual(row?[data: "c"].decimal, Decimal(string: "3.14159265358979")!) - XCTAssertEqual(row?[data: "d"].decimal, Decimal(string: "1234567898765")!) - } - - func testDecimalStringSerialization() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - - XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE IF EXISTS \"table1\"").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery(""" - CREATE TABLE table1 ( - "balance" text NOT NULL - ); - """).wait()) - defer { XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE \"table1\"").wait()) } - - XCTAssertNoThrow(_ = try conn?.query("INSERT INTO table1 VALUES ($1)", [.init(decimal: Decimal(string: "123456.789123")!)]).wait()) - - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - SELECT - "balance" - FROM table1 - """).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "balance"].decimal, Decimal(string: "123456.789123")!) - } - - func testMoney() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - select - '0'::money as a, - '0.05'::money as b, - '0.23'::money as c, - '3.14'::money as d, - '12345678.90'::money as e - """).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "a"].string, "0.00") - XCTAssertEqual(row?[data: "b"].string, "0.05") - XCTAssertEqual(row?[data: "c"].string, "0.23") - XCTAssertEqual(row?[data: "d"].string, "3.14") - XCTAssertEqual(row?[data: "e"].string, "12345678.90") - } - - @available(*, deprecated, message: "Testing deprecated functionality") - func testIntegerArrayParse() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - select - '{1,2,3}'::int[] as array - """).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "array"].array(of: Int.self), [1, 2, 3]) - } - - @available(*, deprecated, message: "Testing deprecated functionality") - func testEmptyIntegerArrayParse() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - select - '{}'::int[] as array - """).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "array"].array(of: Int.self), []) - } - - @available(*, deprecated, message: "Testing deprecated functionality") - func testOptionalIntegerArrayParse() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - select - '{1, 2, NULL, 4}'::int8[] as array - """).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "array"].array(of: Int?.self), [1, 2, nil, 4]) - } - - @available(*, deprecated, message: "Testing deprecated functionality") - func testNullIntegerArrayParse() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - select - null::int[] as array - """).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "array"].array(of: Int.self), nil) - } - - @available(*, deprecated, message: "Testing deprecated functionality") - func testIntegerArraySerialize() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - select - $1::int8[] as array - """, [ - PostgresData(array: [1, 2, 3]) - ]).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "array"].array(of: Int.self), [1, 2, 3]) - } - - @available(*, deprecated, message: "Testing deprecated functionality") - func testEmptyIntegerArraySerialize() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - select - $1::int8[] as array - """, [ - PostgresData(array: [] as [Int]) - ]).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "array"].array(of: Int.self), []) - } - - @available(*, deprecated, message: "Testing deprecated functionality") - func testOptionalIntegerArraySerialize() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - select - $1::int8[] as array - """, [ - PostgresData(array: [1, nil, 3] as [Int64?]) - ]).wait()) - XCTAssertEqual(rows?.count, 1) - let row = rows?.first?.makeRandomAccess() - 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? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - - XCTAssertNoThrow(_ = try conn?.simpleQuery(#"DROP TABLE IF EXISTS "non_null_empty_strings""#).wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery(""" - CREATE TABLE non_null_empty_strings ( - "id" SERIAL, - "nonNullString" text NOT NULL, - PRIMARY KEY ("id") - ); - """).wait()) - defer { XCTAssertNoThrow(_ = try conn?.simpleQuery(#"DROP TABLE "non_null_empty_strings""#).wait()) } - - XCTAssertNoThrow(_ = try conn?.simpleQuery(""" - INSERT INTO non_null_empty_strings ("nonNullString") VALUES ('') - """).wait()) - - var rows: [PostgresRow]? - XCTAssertNoThrow(rows = try conn?.simpleQuery(#"SELECT * FROM "non_null_empty_strings""#).wait()) - XCTAssertEqual(rows?.count, 1) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "nonNullString"].string, "") // <--- this fails - } - - - func testBoolSerialize() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - do { - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query("select $1::bool as bool", [true]).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "bool"].bool, true) - } - do { - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query("select $1::bool as bool", [false]).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "bool"].bool, false) - } - do { - var rows: [PostgresRow]? - XCTAssertNoThrow(rows = try conn?.simpleQuery("select true::bool as bool").wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "bool"].bool, true) - } - do { - var rows: [PostgresRow]? - XCTAssertNoThrow(rows = try conn?.simpleQuery("select false::bool as bool").wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "bool"].bool, false) - } - } - - func testBytesSerialize() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query("select $1::bytea as bytes", [ - PostgresData(bytes: [1, 2, 3]) - ]).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "bytes"].bytes, [1, 2, 3]) - } - - func testJSONBSerialize() { - struct Object: Codable, PostgresCodable { - let foo: Int - let bar: Int - } - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow(try conn?.close().wait()) } - do { - var postgresData: PostgresData? - XCTAssertNoThrow(postgresData = try PostgresData(jsonb: Object(foo: 1, bar: 2))) - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query("select $1::jsonb as jsonb", [XCTUnwrap(postgresData)]).wait()) - - var object: Object? - XCTAssertNoThrow(object = try rows?.first?.decode(Object.self, context: .default)) - XCTAssertEqual(object?.foo, 1) - XCTAssertEqual(object?.bar, 2) - } - - do { - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query("select jsonb_build_object('foo',1,'bar',2) as jsonb").wait()) - - var object: Object? - XCTAssertNoThrow(object = try rows?.first?.decode(Object.self, context: .default)) - XCTAssertEqual(object?.foo, 1) - XCTAssertEqual(object?.bar, 2) - } - } - - func testInt4RangeSerialize() async throws { - let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get() - self.addTeardownBlock { - try await conn.close() - } - do { - let range: Range<Int32> = Int32.min..<Int32.max - var binds = PostgresBindings() - binds.append(range, context: .default) - let query = PostgresQuery( - unsafeSQL: "select $1::int4range as range", - binds: binds - ) - let rowSequence: PostgresRowSequence? = try await conn.query(query, logger: .psqlTest) - var rowIterator: PostgresRowSequence.AsyncIterator? = rowSequence?.makeAsyncIterator() - let row: PostgresRow? = try await rowIterator?.next() - let decodedRange: Range<Int32>? = try row?.decode(Range<Int32>.self, context: .default) - XCTAssertEqual(range, decodedRange) - } - do { - let emptyRange: Range<Int32> = Int32.min..<Int32.min - var binds = PostgresBindings() - binds.append(emptyRange, context: .default) - let query = PostgresQuery( - unsafeSQL: "select $1::int4range as range", - binds: binds - ) - let rowSequence: PostgresRowSequence? = try await conn.query(query, logger: .psqlTest) - var rowIterator: PostgresRowSequence.AsyncIterator? = rowSequence?.makeAsyncIterator() - let row: PostgresRow? = try await rowIterator?.next() - let decodedEmptyRange: Range<Int32>? = try row?.decode(Range<Int32>.self, context: .default) - let expectedRange: Range<Int32> = Int32.valueForEmptyRange..<Int32.valueForEmptyRange - XCTAssertNotEqual(emptyRange, expectedRange) - XCTAssertEqual(expectedRange, decodedEmptyRange) - } - do { - let closedRange: ClosedRange<Int32> = Int32.min...(Int32.max - 1) - var binds = PostgresBindings() - binds.append(closedRange, context: .default) - let query = PostgresQuery( - unsafeSQL: "select $1::int4range as range", - binds: binds - ) - let rowSequence: PostgresRowSequence? = try await conn.query(query, logger: .psqlTest) - var rowIterator: PostgresRowSequence.AsyncIterator? = rowSequence?.makeAsyncIterator() - let row: PostgresRow? = try await rowIterator?.next() - let decodedClosedRange: ClosedRange<Int32>? = try row?.decode(ClosedRange<Int32>.self, context: .default) - XCTAssertEqual(closedRange, decodedClosedRange) - } - } - - func testInt8RangeSerialize() async throws { - let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get() - self.addTeardownBlock { - try await conn.close() - } - do { - let range: Range<Int64> = Int64.min..<Int64.max - var binds = PostgresBindings() - binds.append(range, context: .default) - let query = PostgresQuery( - unsafeSQL: "select $1::int8range as range", - binds: binds - ) - let rowSequence: PostgresRowSequence? = try await conn.query(query, logger: .psqlTest) - var rowIterator: PostgresRowSequence.AsyncIterator? = rowSequence?.makeAsyncIterator() - let row: PostgresRow? = try await rowIterator?.next() - let decodedRange: Range<Int64>? = try row?.decode(Range<Int64>.self, context: .default) - XCTAssertEqual(range, decodedRange) - } - do { - let emptyRange: Range<Int64> = Int64.min..<Int64.min - var binds = PostgresBindings() - binds.append(emptyRange, context: .default) - let query = PostgresQuery( - unsafeSQL: "select $1::int8range as range", - binds: binds - ) - let rowSequence: PostgresRowSequence? = try await conn.query(query, logger: .psqlTest) - var rowIterator: PostgresRowSequence.AsyncIterator? = rowSequence?.makeAsyncIterator() - let row: PostgresRow? = try await rowIterator?.next() - let decodedEmptyRange: Range<Int64>? = try row?.decode(Range<Int64>.self, context: .default) - let expectedRange: Range<Int64> = Int64.valueForEmptyRange..<Int64.valueForEmptyRange - XCTAssertNotEqual(emptyRange, expectedRange) - XCTAssertEqual(expectedRange, decodedEmptyRange) - } - do { - let closedRange: ClosedRange<Int64> = Int64.min...(Int64.max - 1) - var binds = PostgresBindings() - binds.append(closedRange, context: .default) - let query = PostgresQuery( - unsafeSQL: "select $1::int8range as range", - binds: binds - ) - let rowSequence: PostgresRowSequence? = try await conn.query(query, logger: .psqlTest) - var rowIterator: PostgresRowSequence.AsyncIterator? = rowSequence?.makeAsyncIterator() - let row: PostgresRow? = try await rowIterator?.next() - let decodedClosedRange: ClosedRange<Int64>? = try row?.decode(ClosedRange<Int64>.self, context: .default) - XCTAssertEqual(closedRange, decodedClosedRange) - } - } - - func testRemoteTLSServer() { - // postgres://uymgphwj:7_tHbREdRwkqAdu4KoIS7hQnNxr8J1LA@elmer.db.elephantsql.com:5432/uymgphwj - var conn: PostgresConnection? - let logger = Logger(label: "test") - let sslContext = try! NIOSSLContext(configuration: .makeClientConfiguration()) - let config = PostgresConnection.Configuration( - host: "elmer.db.elephantsql.com", - port: 5432, - username: "uymgphwj", - password: "7_tHbREdRwkqAdu4KoIS7hQnNxr8J1LA", - database: "uymgphwj", - tls: .require(sslContext) - ) - XCTAssertNoThrow(conn = try PostgresConnection.connect(on: eventLoop, configuration: config, id: 0, logger: logger).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: [PostgresRow]? - XCTAssertNoThrow(rows = try conn?.simpleQuery("SELECT version()").wait()) - XCTAssertEqual(rows?.count, 1) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "version"].string?.contains("PostgreSQL"), true) - } - - @available(*, deprecated, message: "Test deprecated functionality") - func testFailingTLSConnectionClosesConnection() { - // There was a bug (https://github.com/vapor/postgres-nio/issues/133) where we would hit - // an assert because we didn't close the connection. This test should succeed without hitting - // the assert - - // postgres://uymgphwj:7_tHbREdRwkqAdu4KoIS7hQnNxr8J1LA@elmer.db.elephantsql.com:5432/uymgphwj - - // We should get an error because you can't use an IP address for SNI, but we shouldn't bomb out by - // hitting the assert - XCTAssertThrowsError( - try PostgresConnection.connect( - to: SocketAddress.makeAddressResolvingHost("elmer.db.elephantsql.com", port: 5432), - tlsConfiguration: .makeClientConfiguration(), - serverHostname: "34.228.73.168", - on: eventLoop - ).wait() - ) - // If we hit this, we're all good - XCTAssertTrue(true) - } - - @available(*, deprecated, message: "Test deprecated functionality") - func testInvalidPassword() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.testUnauthenticated(on: eventLoop).wait()) - let authFuture = conn?.authenticate(username: "invalid", database: "invalid", password: "bad") - XCTAssertThrowsError(_ = try authFuture?.wait()) { error in - XCTAssert((error as? PostgresError)?.code == .invalidPassword || (error as? PostgresError)?.code == .invalidAuthorizationSpecification) - } - - // in this case the connection will be closed by the remote - XCTAssertNoThrow(try conn?.closeFuture.wait()) - } - - func testColumnsInJoin() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - - let dateInTable1 = Date(timeIntervalSince1970: 1234) - let dateInTable2 = Date(timeIntervalSince1970: 5678) - XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE IF EXISTS \"table1\"").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery(""" - CREATE TABLE table1 ( - "id" int8 NOT NULL, - "table2_id" int8, - "intValue" int8, - "stringValue" text, - "dateValue" timestamptz, - PRIMARY KEY ("id") - ); - """).wait()) - defer { XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE \"table1\"").wait()) } - - XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE IF EXISTS \"table2\"").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery(""" - CREATE TABLE table2 ( - "id" int8 NOT NULL, - "intValue" int8, - "stringValue" text, - "dateValue" timestamptz, - PRIMARY KEY ("id") - ); - """).wait()) - defer { XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE \"table2\"").wait()) } - - XCTAssertNoThrow(_ = try conn?.simpleQuery("INSERT INTO table1 VALUES (12, 34, 56, 'stringInTable1', to_timestamp(1234))").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("INSERT INTO table2 VALUES (34, 78, 'stringInTable2', to_timestamp(5678))").wait()) - - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - SELECT - "table1"."id" as "t1_id", - "table1"."intValue" as "t1_intValue", - "table1"."dateValue" as "t1_dateValue", - "table1"."stringValue" as "t1_stringValue", - "table2"."id" as "t2_id", - "table2"."intValue" as "t2_intValue", - "table2"."dateValue" as "t2_dateValue", - "table2"."stringValue" as "t2_stringValue", - * - FROM table1 INNER JOIN table2 ON table1.table2_id = table2.id - """).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "t1_id"].int, 12) - XCTAssertEqual(row?[data: "table2_id"].int, 34) - XCTAssertEqual(row?[data: "t1_intValue"].int, 56) - XCTAssertEqual(row?[data: "t1_stringValue"].string, "stringInTable1") - XCTAssertEqual(row?[data: "t1_dateValue"].date, dateInTable1) - XCTAssertEqual(row?[data: "t2_id"].int, 34) - XCTAssertEqual(row?[data: "t2_intValue"].int, 78) - XCTAssertEqual(row?[data: "t2_stringValue"].string, "stringInTable2") - XCTAssertEqual(row?[data: "t2_dateValue"].date, dateInTable2) - } - - @available(*, deprecated, message: "Testing deprecated functionality") - func testStringArrays() { - let query = """ - SELECT - $1::uuid as "id", - $2::bigint as "revision", - $3::timestamp as "updated_at", - $4::timestamp as "created_at", - $5::text as "name", - $6::text[] as "countries", - $7::text[] as "languages", - $8::text[] as "currencies" - """ - - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(query, [ - PostgresData(uuid: UUID(uuidString: "D2710E16-EB07-4FD6-A87E-B1BE41C9BD3D")!), - PostgresData(int: Int(0)), - PostgresData(date: Date(timeIntervalSince1970: 0)), - PostgresData(date: Date(timeIntervalSince1970: 0)), - PostgresData(string: "Foo"), - PostgresData(array: ["US"]), - PostgresData(array: ["en"]), - PostgresData(array: ["USD", "DKK"]), - ]).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "countries"].array(of: String.self), ["US"]) - XCTAssertEqual(row?[data: "languages"].array(of: String.self), ["en"]) - XCTAssertEqual(row?[data: "currencies"].array(of: String.self), ["USD", "DKK"]) - } - - func testBindDate() { - // https://github.com/vapor/postgres-nio/issues/53 - let date = Date(timeIntervalSince1970: 1571425782) - let query = """ - SELECT $1::json as "date" - """ - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - XCTAssertThrowsError(_ = try conn?.query(query, [.init(date: date)]).wait()) { error in - guard let postgresError = try? XCTUnwrap(error as? PostgresError) else { return } - guard case let .server(serverError) = postgresError else { - XCTFail("Expected a .serverError but got \(postgresError)") - return - } - XCTAssertEqual(serverError.fields[.routine], "transformTypeCast") - } - - } - - func testBindCharString() { - // https://github.com/vapor/postgres-nio/issues/53 - let query = """ - SELECT $1::char as "char" - """ - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(query, [.init(string: "f")]).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "char"].string, "f") - } - - func testBindCharUInt8() { - // https://github.com/vapor/postgres-nio/issues/53 - let query = """ - SELECT $1::char as "char" - """ - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(query, [.init(uint8: 42)]).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "char"].string, "*") - } - - @available(*, deprecated, message: "Testing deprecated functionality") - func testDoubleArraySerialization() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - let doubles: [Double] = [3.14, 42] - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - select - $1::double precision[] as doubles - """, [ - .init(array: doubles) - ]).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "doubles"].array(of: Double.self), doubles) - } - - // https://github.com/vapor/postgres-nio/issues/42 - func testUInt8Serialization() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - select - $1::"char" as int - """, [ - .init(uint8: 5) - ]).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "int"].uint8, 5) - } - - func testPreparedQuery() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var prepared: PreparedQuery? - XCTAssertNoThrow(prepared = try conn?.prepare(query: "SELECT 1 as one;").wait()) - var rows: [PostgresRow]? - XCTAssertNoThrow(rows = try prepared?.execute().wait()) - - XCTAssertEqual(rows?.count, 1) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "one"].int, 1) - } - - func testPrepareQueryClosure() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var queries: [[PostgresRow]]? - XCTAssertNoThrow(queries = try conn?.prepare(query: "SELECT $1::text as foo;", handler: { [eventLoop] query in - let a = query.execute(["a"]) - let b = query.execute(["b"]) - let c = query.execute(["c"]) - return EventLoopFuture.whenAllSucceed([a, b, c], on: eventLoop) - }).wait()) - XCTAssertEqual(queries?.count, 3) - var resultIterator = queries?.makeIterator() - XCTAssertEqual(try resultIterator?.next()?.first?.decode(String.self, context: .default), "a") - XCTAssertEqual(try resultIterator?.next()?.first?.decode(String.self, context: .default), "b") - XCTAssertEqual(try resultIterator?.next()?.first?.decode(String.self, context: .default), "c") - } - - // https://github.com/vapor/postgres-nio/issues/122 - func testPreparedQueryNoResults() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - - XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE IF EXISTS \"table_no_results\"").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery(""" - CREATE TABLE table_no_results ( - "id" int8 NOT NULL, - "stringValue" text, - PRIMARY KEY ("id") - ); - """).wait()) - defer { XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE \"table_no_results\"").wait()) } - - XCTAssertNoThrow(_ = try conn?.prepare(query: "DELETE FROM \"table_no_results\" WHERE id = $1").wait()) - } - - - // https://github.com/vapor/postgres-nio/issues/71 - func testChar1Serialization() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - select - '5'::char(1) as one, - '5'::char(2) as two - """).wait()) - - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "one"].uint8, 53) - XCTAssertEqual(row?[data: "one"].int16, 53) - XCTAssertEqual(row?[data: "one"].string, "5") - XCTAssertEqual(row?[data: "two"].uint8, nil) - XCTAssertEqual(row?[data: "two"].int16, nil) - XCTAssertEqual(row?[data: "two"].string, "5 ") - } - - func testUserDefinedType() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - - XCTAssertNoThrow(_ = try conn?.query("DROP TYPE IF EXISTS foo").wait()) - XCTAssertNoThrow(_ = try conn?.query("CREATE TYPE foo AS ENUM ('bar', 'qux')").wait()) - defer { - XCTAssertNoThrow(_ = try conn?.query("DROP TYPE foo").wait()) - } - var res: PostgresQueryResult? - XCTAssertNoThrow(res = try conn?.query("SELECT 'qux'::foo as foo").wait()) - let row = res?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "foo"].string, "qux") - } - - @available(*, deprecated, message: "Testing deprecated functionality") - func testNullBind() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - - var res: PostgresQueryResult? - XCTAssertNoThrow(res = try conn?.query("SELECT $1::text as foo", [String?.none.postgresData!]).wait()) - let row = res?.first?.makeRandomAccess() - XCTAssertNil(row?[data: "foo"].string) - } - - func testUpdateMetadata() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE IF EXISTS test_table").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("CREATE TABLE test_table(pk int PRIMARY KEY)").wait()) - XCTAssertNoThrow(_ = try conn?.simpleQuery("INSERT INTO test_table VALUES(1)").wait()) - XCTAssertNoThrow(try conn?.query("DELETE FROM test_table", onMetadata: { metadata in - XCTAssertEqual(metadata.command, "DELETE") - XCTAssertEqual(metadata.oid, nil) - XCTAssertEqual(metadata.rows, 1) - }, onRow: { _ in }).wait()) - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query("DELETE FROM test_table").wait()) - XCTAssertEqual(rows?.metadata.command, "DELETE") - XCTAssertEqual(rows?.metadata.oid, nil) - XCTAssertEqual(rows?.metadata.rows, 0) - } - - func testTooManyBinds() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - let binds = [PostgresData].init(repeating: .null, count: Int(UInt16.max) + 1) - XCTAssertThrowsError(try conn?.query("SELECT version()", binds).wait()) { error in - guard case .tooManyParameters = (error as? PSQLError)?.code.base else { - return XCTFail("Unexpected error: \(error)") - } - } - } - - func testRemoteClose() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - XCTAssertNoThrow( try conn?.channel.close().wait() ) - } - - // https://github.com/vapor/postgres-nio/issues/113 - @available(*, deprecated, message: "Testing deprecated functionality") - func testVaryingCharArray() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - - var res: PostgresQueryResult? - XCTAssertNoThrow(res = try conn?.query(#"SELECT '{"foo", "bar", "baz"}'::VARCHAR[] as foo"#).wait()) - let row = res?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "foo"].array(of: String.self), ["foo", "bar", "baz"]) - } - - // https://github.com/vapor/postgres-nio/issues/115 - func testSetTimeZone() { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - - XCTAssertNoThrow(_ = try conn?.simpleQuery("SET TIME ZONE INTERVAL '+5:45' HOUR TO MINUTE").wait()) - XCTAssertNoThrow(_ = try conn?.query("SET TIME ZONE INTERVAL '+5:45' HOUR TO MINUTE").wait()) - } - - func testIntegerConversions() throws { - var conn: PostgresConnection? - XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) - defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var rows: PostgresQueryResult? - XCTAssertNoThrow(rows = try conn?.query(""" - select - 'a'::char as test8, - - '-32768'::smallint as min16, - '32767'::smallint as max16, - - '-2147483648'::integer as min32, - '2147483647'::integer as max32, - - '-9223372036854775808'::bigint as min64, - '9223372036854775807'::bigint as max64 - """).wait()) - let row = rows?.first?.makeRandomAccess() - XCTAssertEqual(row?[data: "test8"].uint8, 97) - XCTAssertEqual(row?[data: "test8"].int16, 97) - XCTAssertEqual(row?[data: "test8"].int32, 97) - XCTAssertEqual(row?[data: "test8"].int64, 97) - - XCTAssertEqual(row?[data: "min16"].uint8, nil) - XCTAssertEqual(row?[data: "max16"].uint8, nil) - XCTAssertEqual(row?[data: "min16"].int16, .min) - XCTAssertEqual(row?[data: "max16"].int16, .max) - XCTAssertEqual(row?[data: "min16"].int32, -32768) - XCTAssertEqual(row?[data: "max16"].int32, 32767) - XCTAssertEqual(row?[data: "min16"].int64, -32768) - XCTAssertEqual(row?[data: "max16"].int64, 32767) - - XCTAssertEqual(row?[data: "min32"].uint8, nil) - XCTAssertEqual(row?[data: "max32"].uint8, nil) - XCTAssertEqual(row?[data: "min32"].int16, nil) - XCTAssertEqual(row?[data: "max32"].int16, nil) - XCTAssertEqual(row?[data: "min32"].int32, .min) - XCTAssertEqual(row?[data: "max32"].int32, .max) - XCTAssertEqual(row?[data: "min32"].int64, -2147483648) - XCTAssertEqual(row?[data: "max32"].int64, 2147483647) - - XCTAssertEqual(row?[data: "min64"].uint8, nil) - XCTAssertEqual(row?[data: "max64"].uint8, nil) - XCTAssertEqual(row?[data: "min64"].int16, nil) - XCTAssertEqual(row?[data: "max64"].int16, nil) - XCTAssertEqual(row?[data: "min64"].int32, nil) - XCTAssertEqual(row?[data: "max64"].int32, nil) - XCTAssertEqual(row?[data: "min64"].int64, .min) - XCTAssertEqual(row?[data: "max64"].int64, .max) - } +// func testSimpleQueryVersion() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: [PostgresRow]? +// XCTAssertNoThrow(rows = try conn?.simpleQuery("SELECT version()").wait()) +// XCTAssertEqual(rows?.count, 1) +// XCTAssertEqual(try rows?.first?.decode(String.self, context: .default).contains("PostgreSQL"), true) +// } +// +// func testSimpleQueryVersionUsingUDS() throws { +// try XCTSkipUnless(env("POSTGRES_SOCKET") != nil) +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.testUDS(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: [PostgresRow]? +// XCTAssertNoThrow(rows = try conn?.simpleQuery("SELECT version()").wait()) +// XCTAssertEqual(rows?.count, 1) +// XCTAssertEqual(try rows?.first?.decode(String.self, context: .default).contains("PostgreSQL"), true) +// } +// +// func testSimpleQueryVersionUsingEstablishedChannel() throws { +// let channel = try ClientBootstrap(group: self.group).connect(to: PostgresConnection.address()).wait() +// let conn = try PostgresConnection.testChannel(channel, on: self.eventLoop).wait() +// defer { XCTAssertNoThrow(try conn.close().wait()) } +// +// let rows = try conn.simpleQuery("SELECT version()").wait() +// XCTAssertEqual(rows.count, 1) +// XCTAssertEqual(try rows.first?.decode(String.self, context: .default).contains("PostgreSQL"), true) +// } +// +// func testQueryVersion() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query("SELECT version()", .init()).wait()) +// XCTAssertEqual(rows?.count, 1) +// XCTAssertEqual(try rows?.first?.decode(String.self, context: .default).contains("PostgreSQL"), true) +// } +// +// func testQuerySelectParameter() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query("SELECT $1::TEXT as foo", ["hello"]).wait()) +// XCTAssertEqual(rows?.count, 1) +// XCTAssertEqual(try rows?.first?.decode(String.self, context: .default), "hello") +// } +// +// func testSQLError() throws { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// XCTAssertThrowsError(_ = try conn?.simpleQuery("SELECT &").wait()) { error in +// XCTAssertEqual((error as? PostgresError)?.code, .syntaxError) +// } +// } +// +// func testNotificationsEmptyPayload() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// var receivedNotifications: [PostgresMessage.NotificationResponse] = [] +// conn?.addListener(channel: "example") { context, notification in +// receivedNotifications.append(notification) +// } +// XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) +// // Notifications are asynchronous, so we should run at least one more query to make sure we'll have received the notification response by then +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) +// XCTAssertEqual(receivedNotifications.count, 1) +// XCTAssertEqual(receivedNotifications.first?.channel, "example") +// XCTAssertEqual(receivedNotifications.first?.payload, "") +// } +// +// func testNotificationsNonEmptyPayload() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var receivedNotifications: [PostgresMessage.NotificationResponse] = [] +// conn?.addListener(channel: "example") { context, notification in +// receivedNotifications.append(notification) +// } +// XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example, 'Notification payload example'").wait()) +// // Notifications are asynchronous, so we should run at least one more query to make sure we'll have received the notification response by then +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) +// XCTAssertEqual(receivedNotifications.count, 1) +// XCTAssertEqual(receivedNotifications.first?.channel, "example") +// XCTAssertEqual(receivedNotifications.first?.payload, "Notification payload example") +// } +// +// func testNotificationsRemoveHandlerWithinHandler() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var receivedNotifications = 0 +// conn?.addListener(channel: "example") { context, notification in +// receivedNotifications += 1 +// context.stop() +// } +// XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) +// XCTAssertEqual(receivedNotifications, 1) +// } +// +// func testNotificationsRemoveHandlerOutsideHandler() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var receivedNotifications = 0 +// let context = conn?.addListener(channel: "example") { context, notification in +// receivedNotifications += 1 +// } +// XCTAssertNotNil(context) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) +// context?.stop() +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) +// XCTAssertEqual(receivedNotifications, 1) +// } +// +// func testNotificationsMultipleRegisteredHandlers() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var receivedNotifications1 = 0 +// conn?.addListener(channel: "example") { context, notification in +// receivedNotifications1 += 1 +// } +// var receivedNotifications2 = 0 +// conn?.addListener(channel: "example") { context, notification in +// receivedNotifications2 += 1 +// } +// XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) +// XCTAssertEqual(receivedNotifications1, 1) +// XCTAssertEqual(receivedNotifications2, 1) +// } +// +// func testNotificationsMultipleRegisteredHandlersRemoval() throws { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var receivedNotifications1 = 0 +// XCTAssertNotNil(conn?.addListener(channel: "example") { context, notification in +// receivedNotifications1 += 1 +// context.stop() +// }) +// var receivedNotifications2 = 0 +// XCTAssertNotNil(conn?.addListener(channel: "example") { context, notification in +// receivedNotifications2 += 1 +// }) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) +// XCTAssertEqual(receivedNotifications1, 1) +// XCTAssertEqual(receivedNotifications2, 2) +// } +// +// func testNotificationHandlerFiltersOnChannel() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// XCTAssertNotNil(conn?.addListener(channel: "desired") { context, notification in +// XCTFail("Received notification on channel that handler was not registered for") +// }) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN undesired").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY undesired").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) +// } +// +// func testSelectTypes() throws { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var results: [PostgresRow]? +// XCTAssertNoThrow(results = try conn?.simpleQuery("SELECT * FROM pg_type").wait()) +// XCTAssert((results?.count ?? 0) > 350, "Results count not large enough") +// } +// +// func testSelectType() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var results: [PostgresRow]? +// XCTAssertNoThrow(results = try conn?.simpleQuery("SELECT * FROM pg_type WHERE typname = 'float8'").wait()) +// // [ +// // "typreceive": "float8recv", +// // "typelem": "0", +// // "typarray": "1022", +// // "typalign": "d", +// // "typanalyze": "-", +// // "typtypmod": "-1", +// // "typname": "float8", +// // "typnamespace": "11", +// // "typdefault": "<null>", +// // "typdefaultbin": "<null>", +// // "typcollation": "0", +// // "typispreferred": "t", +// // "typrelid": "0", +// // "typbyval": "t", +// // "typnotnull": "f", +// // "typinput": "float8in", +// // "typlen": "8", +// // "typcategory": "N", +// // "typowner": "10", +// // "typtype": "b", +// // "typdelim": ",", +// // "typndims": "0", +// // "typbasetype": "0", +// // "typacl": "<null>", +// // "typisdefined": "t", +// // "typmodout": "-", +// // "typmodin": "-", +// // "typsend": "float8send", +// // "typstorage": "p", +// // "typoutput": "float8out" +// // ] +// XCTAssertEqual(results?.count, 1) +// let row = results?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "typname"].string, "float8") +// XCTAssertEqual(row?[data: "typnamespace"].int, 11) +// XCTAssertEqual(row?[data: "typowner"].int, 10) +// XCTAssertEqual(row?[data: "typlen"].int, 8) +// } +// +// func testIntegers() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// struct Integers: Decodable { +// let smallint: Int16 +// let smallint_min: Int16 +// let smallint_max: Int16 +// let int: Int32 +// let int_min: Int32 +// let int_max: Int32 +// let bigint: Int64 +// let bigint_min: Int64 +// let bigint_max: Int64 +// } +// var results: PostgresQueryResult? +// XCTAssertNoThrow(results = try conn?.query(""" +// SELECT +// 1::SMALLINT as smallint, +// -32767::SMALLINT as smallint_min, +// 32767::SMALLINT as smallint_max, +// 1::INT as int, +// -2147483647::INT as int_min, +// 2147483647::INT as int_max, +// 1::BIGINT as bigint, +// -9223372036854775807::BIGINT as bigint_min, +// 9223372036854775807::BIGINT as bigint_max +// """).wait()) +// XCTAssertEqual(results?.count, 1) +// +// let row = results?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "smallint"].int16, 1) +// XCTAssertEqual(row?[data: "smallint_min"].int16, -32_767) +// XCTAssertEqual(row?[data: "smallint_max"].int16, 32_767) +// XCTAssertEqual(row?[data: "int"].int32, 1) +// XCTAssertEqual(row?[data: "int_min"].int32, -2_147_483_647) +// XCTAssertEqual(row?[data: "int_max"].int32, 2_147_483_647) +// XCTAssertEqual(row?[data: "bigint"].int64, 1) +// XCTAssertEqual(row?[data: "bigint_min"].int64, -9_223_372_036_854_775_807) +// XCTAssertEqual(row?[data: "bigint_max"].int64, 9_223_372_036_854_775_807) +// } +// +// func testPi() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// struct Pi: Decodable { +// let text: String +// let numeric_string: String +// let numeric_decimal: Decimal +// let double: Double +// let float: Float +// } +// var results: PostgresQueryResult? +// XCTAssertNoThrow(results = try conn?.query(""" +// SELECT +// pi()::TEXT as text, +// pi()::NUMERIC as numeric_string, +// pi()::NUMERIC as numeric_decimal, +// pi()::FLOAT8 as double, +// pi()::FLOAT4 as float +// """).wait()) +// XCTAssertEqual(results?.count, 1) +// let row = results?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "text"].string?.hasPrefix("3.14159265"), true) +// XCTAssertEqual(row?[data: "numeric_string"].string?.hasPrefix("3.14159265"), true) +// XCTAssertTrue(row?[data: "numeric_decimal"].decimal?.isLess(than: 3.14159265358980) ?? false) +// XCTAssertFalse(row?[data: "numeric_decimal"].decimal?.isLess(than: 3.14159265358978) ?? true) +// XCTAssertTrue(row?[data: "double"].double?.description.hasPrefix("3.141592") ?? false) +// XCTAssertTrue(row?[data: "float"].float?.description.hasPrefix("3.141592") ?? false) +// } +// +// func testUUID() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// struct Model: Decodable { +// let id: UUID +// let string: String +// } +// var results: PostgresQueryResult? +// XCTAssertNoThrow(results = try conn?.query(""" +// SELECT +// '123e4567-e89b-12d3-a456-426655440000'::UUID as id, +// '123e4567-e89b-12d3-a456-426655440000'::UUID as string +// """).wait()) +// XCTAssertEqual(results?.count, 1) +// let row = results?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "id"].uuid, UUID(uuidString: "123E4567-E89B-12D3-A456-426655440000")) +// XCTAssertEqual(UUID(uuidString: row?[data: "id"].string ?? ""), UUID(uuidString: "123E4567-E89B-12D3-A456-426655440000")) +// } +// +// func testInt4Range() async throws { +// let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get() +// self.addTeardownBlock { +// try await conn.close() +// } +// struct Model: Decodable { +// let range: Range<Int32> +// } +// let results1: PostgresQueryResult = try await conn.query(""" +// SELECT +// '[\(Int32.min), \(Int32.max))'::int4range AS range +// """).get() +// XCTAssertEqual(results1.count, 1) +// var row = results1.first?.makeRandomAccess() +// let expectedRange: Range<Int32> = Int32.min..<Int32.max +// let decodedRange = try row?.decode(column: "range", as: Range<Int32>.self, context: .default) +// XCTAssertEqual(decodedRange, expectedRange) +// +// let results2 = try await conn.query(""" +// SELECT +// ARRAY[ +// '[0, 1)'::int4range, +// '[10, 11)'::int4range +// ] AS ranges +// """).get() +// XCTAssertEqual(results2.count, 1) +// row = results2.first?.makeRandomAccess() +// let decodedRangeArray = try row?.decode(column: "ranges", as: [Range<Int32>].self, context: .default) +// let decodedClosedRangeArray = try row?.decode(column: "ranges", as: [ClosedRange<Int32>].self, context: .default) +// XCTAssertEqual(decodedRangeArray, [0..<1, 10..<11]) +// XCTAssertEqual(decodedClosedRangeArray, [0...0, 10...10]) +// } +// +// func testEmptyInt4Range() async throws { +// let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get() +// self.addTeardownBlock { +// try await conn.close() +// } +// struct Model: Decodable { +// let range: Range<Int32> +// } +// let randomValue = Int32.random(in: Int32.min...Int32.max) +// let results: PostgresQueryResult = try await conn.query(""" +// SELECT +// '[\(randomValue),\(randomValue))'::int4range AS range +// """).get() +// XCTAssertEqual(results.count, 1) +// let row = results.first?.makeRandomAccess() +// let expectedRange: Range<Int32> = Int32.valueForEmptyRange..<Int32.valueForEmptyRange +// let decodedRange = try row?.decode(column: "range", as: Range<Int32>.self, context: .default) +// XCTAssertEqual(decodedRange, expectedRange) +// +// XCTAssertThrowsError( +// try row?.decode(column: "range", as: ClosedRange<Int32>.self, context: .default) +// ) +// } +// +// func testInt8Range() async throws { +// let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get() +// self.addTeardownBlock { +// try await conn.close() +// } +// struct Model: Decodable { +// let range: Range<Int64> +// } +// let results1: PostgresQueryResult = try await conn.query(""" +// SELECT +// '[\(Int64.min), \(Int64.max))'::int8range AS range +// """).get() +// XCTAssertEqual(results1.count, 1) +// var row = results1.first?.makeRandomAccess() +// let expectedRange: Range<Int64> = Int64.min..<Int64.max +// let decodedRange = try row?.decode(column: "range", as: Range<Int64>.self, context: .default) +// XCTAssertEqual(decodedRange, expectedRange) +// +// let results2: PostgresQueryResult = try await conn.query(""" +// SELECT +// ARRAY[ +// '[0, 1)'::int8range, +// '[10, 11)'::int8range +// ] AS ranges +// """).get() +// XCTAssertEqual(results2.count, 1) +// row = results2.first?.makeRandomAccess() +// let decodedRangeArray = try row?.decode(column: "ranges", as: [Range<Int64>].self, context: .default) +// let decodedClosedRangeArray = try row?.decode(column: "ranges", as: [ClosedRange<Int64>].self, context: .default) +// XCTAssertEqual(decodedRangeArray, [0..<1, 10..<11]) +// XCTAssertEqual(decodedClosedRangeArray, [0...0, 10...10]) +// } +// +// func testEmptyInt8Range() async throws { +// let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get() +// self.addTeardownBlock { +// try await conn.close() +// } +// struct Model: Decodable { +// let range: Range<Int64> +// } +// let randomValue = Int64.random(in: Int64.min...Int64.max) +// let results: PostgresQueryResult = try await conn.query(""" +// SELECT +// '[\(randomValue),\(randomValue))'::int8range AS range +// """).get() +// XCTAssertEqual(results.count, 1) +// let row = results.first?.makeRandomAccess() +// let expectedRange: Range<Int64> = Int64.valueForEmptyRange..<Int64.valueForEmptyRange +// let decodedRange = try row?.decode(column: "range", as: Range<Int64>.self, context: .default) +// XCTAssertEqual(decodedRange, expectedRange) +// +// XCTAssertThrowsError( +// try row?.decode(column: "range", as: ClosedRange<Int64>.self, context: .default) +// ) +// } +// +// func testDates() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// struct Dates: Decodable { +// var date: Date +// var timestamp: Date +// var timestamptz: Date +// } +// var results: PostgresQueryResult? +// XCTAssertNoThrow(results = try conn?.query(""" +// SELECT +// '2016-01-18 01:02:03 +0042'::DATE as date, +// '2016-01-18 01:02:03 +0042'::TIMESTAMP as timestamp, +// '2016-01-18 01:02:03 +0042'::TIMESTAMPTZ as timestamptz +// """).wait()) +// XCTAssertEqual(results?.count, 1) +// let row = results?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "date"].date?.description, "2016-01-18 00:00:00 +0000") +// XCTAssertEqual(row?[data: "timestamp"].date?.description, "2016-01-18 01:02:03 +0000") +// XCTAssertEqual(row?[data: "timestamptz"].date?.description, "2016-01-18 00:20:03 +0000") +// } +// +// /// https://github.com/vapor/nio-postgres/issues/20 +// func testBindInteger() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// XCTAssertNoThrow(_ = try conn?.simpleQuery("drop table if exists person;").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("create table person(id serial primary key, first_name text, last_name text);").wait()) +// defer { XCTAssertNoThrow(_ = try conn?.simpleQuery("drop table person;").wait()) } +// let id = PostgresData(int32: 5) +// XCTAssertNoThrow(_ = try conn?.query("SELECT id, first_name, last_name FROM person WHERE id = $1", [id]).wait()) +// } +// +// // https://github.com/vapor/nio-postgres/issues/21 +// func testAverageLengthNumeric() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var results: PostgresQueryResult? +// XCTAssertNoThrow(results = try conn?.query("select avg(length('foo')) as average_length").wait()) +// let row = results?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: 0].double, 3.0) +// } +// +// func testNumericParsing() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// '1234.5678'::numeric as a, +// '-123.456'::numeric as b, +// '123456.789123'::numeric as c, +// '3.14159265358979'::numeric as d, +// '10000'::numeric as e, +// '0.00001'::numeric as f, +// '100000000'::numeric as g, +// '0.000000001'::numeric as h, +// '100000000000'::numeric as i, +// '0.000000000001'::numeric as j, +// '123000000000'::numeric as k, +// '0.000000000123'::numeric as l, +// '0.5'::numeric as m +// """).wait()) +// XCTAssertEqual(rows?.count, 1) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "a"].string, "1234.5678") +// XCTAssertEqual(row?[data: "b"].string, "-123.456") +// XCTAssertEqual(row?[data: "c"].string, "123456.789123") +// XCTAssertEqual(row?[data: "d"].string, "3.14159265358979") +// XCTAssertEqual(row?[data: "e"].string, "10000") +// XCTAssertEqual(row?[data: "f"].string, "0.00001") +// XCTAssertEqual(row?[data: "g"].string, "100000000") +// XCTAssertEqual(row?[data: "h"].string, "0.000000001") +// XCTAssertEqual(row?[data: "k"].string, "123000000000") +// XCTAssertEqual(row?[data: "l"].string, "0.000000000123") +// XCTAssertEqual(row?[data: "m"].string, "0.5") +// } +// +// func testSingleNumericParsing() { +// // this seemingly duped test is useful for debugging numeric parsing +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// let numeric = "790226039477542363.6032384900176272473" +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// '\(numeric)'::numeric as n +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "n"].string, numeric) +// } +// +// func testRandomlyGeneratedNumericParsing() throws { +// // this test takes a long time to run +// try XCTSkipUnless(Self.shouldRunLongRunningTests) +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// for _ in 0..<1_000_000 { +// let integer = UInt.random(in: UInt.min..<UInt.max) +// let fraction = UInt.random(in: UInt.min..<UInt.max) +// let number = "\(integer).\(fraction)" +// .trimmingCharacters(in: CharacterSet(["0"])) +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// '\(number)'::numeric as n +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "n"].string, number) +// } +// } +// +// func testNumericSerialization() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// let a = PostgresNumeric(string: "123456.789123")! +// let b = PostgresNumeric(string: "-123456.789123")! +// let c = PostgresNumeric(string: "3.14159265358979")! +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// $1::numeric as a, +// $2::numeric as b, +// $3::numeric as c +// """, [ +// .init(numeric: a), +// .init(numeric: b), +// .init(numeric: c) +// ]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "a"].decimal, Decimal(string: "123456.789123")!) +// XCTAssertEqual(row?[data: "b"].decimal, Decimal(string: "-123456.789123")!) +// XCTAssertEqual(row?[data: "c"].decimal, Decimal(string: "3.14159265358979")!) +// } +// +// func testDecimalStringSerialization() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE IF EXISTS \"table1\"").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery(""" +// CREATE TABLE table1 ( +// "balance" text NOT NULL +// ); +// """).wait()) +// defer { XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE \"table1\"").wait()) } +// +// XCTAssertNoThrow(_ = try conn?.query("INSERT INTO table1 VALUES ($1)", [.init(decimal: Decimal(string: "123456.789123")!)]).wait()) +// +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// SELECT +// "balance" +// FROM table1 +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "balance"].decimal, Decimal(string: "123456.789123")!) +// } +// +// func testMoney() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// '0'::money as a, +// '0.05'::money as b, +// '0.23'::money as c, +// '3.14'::money as d, +// '12345678.90'::money as e +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "a"].string, "0.00") +// XCTAssertEqual(row?[data: "b"].string, "0.05") +// XCTAssertEqual(row?[data: "c"].string, "0.23") +// XCTAssertEqual(row?[data: "d"].string, "3.14") +// XCTAssertEqual(row?[data: "e"].string, "12345678.90") +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testIntegerArrayParse() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// '{1,2,3}'::int[] as array +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "array"].array(of: Int.self), [1, 2, 3]) +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testEmptyIntegerArrayParse() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// '{}'::int[] as array +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "array"].array(of: Int.self), []) +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testOptionalIntegerArrayParse() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// '{1, 2, NULL, 4}'::int8[] as array +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "array"].array(of: Int?.self), [1, 2, nil, 4]) +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testNullIntegerArrayParse() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// null::int[] as array +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "array"].array(of: Int.self), nil) +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testIntegerArraySerialize() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// $1::int8[] as array +// """, [ +// PostgresData(array: [1, 2, 3]) +// ]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "array"].array(of: Int.self), [1, 2, 3]) +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testEmptyIntegerArraySerialize() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// $1::int8[] as array +// """, [ +// PostgresData(array: [] as [Int]) +// ]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "array"].array(of: Int.self), []) +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testOptionalIntegerArraySerialize() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// $1::int8[] as array +// """, [ +// PostgresData(array: [1, nil, 3] as [Int64?]) +// ]).wait()) +// XCTAssertEqual(rows?.count, 1) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "array"].array(of: Int64?.self), [1, nil, 3]) +// } +// +// // https://github.com/vapor/postgres-nio/issues/143 +// func testEmptyStringFromNonNullColumn() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// XCTAssertNoThrow(_ = try conn?.simpleQuery(#"DROP TABLE IF EXISTS "non_null_empty_strings""#).wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery(""" +// CREATE TABLE non_null_empty_strings ( +// "id" SERIAL, +// "nonNullString" text NOT NULL, +// PRIMARY KEY ("id") +// ); +// """).wait()) +// defer { XCTAssertNoThrow(_ = try conn?.simpleQuery(#"DROP TABLE "non_null_empty_strings""#).wait()) } +// +// XCTAssertNoThrow(_ = try conn?.simpleQuery(""" +// INSERT INTO non_null_empty_strings ("nonNullString") VALUES ('') +// """).wait()) +// +// var rows: [PostgresRow]? +// XCTAssertNoThrow(rows = try conn?.simpleQuery(#"SELECT * FROM "non_null_empty_strings""#).wait()) +// XCTAssertEqual(rows?.count, 1) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "nonNullString"].string, "") // <--- this fails +// } +// +// +// func testBoolSerialize() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// do { +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query("select $1::bool as bool", [true]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "bool"].bool, true) +// } +// do { +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query("select $1::bool as bool", [false]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "bool"].bool, false) +// } +// do { +// var rows: [PostgresRow]? +// XCTAssertNoThrow(rows = try conn?.simpleQuery("select true::bool as bool").wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "bool"].bool, true) +// } +// do { +// var rows: [PostgresRow]? +// XCTAssertNoThrow(rows = try conn?.simpleQuery("select false::bool as bool").wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "bool"].bool, false) +// } +// } +// +// func testBytesSerialize() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query("select $1::bytea as bytes", [ +// PostgresData(bytes: [1, 2, 3]) +// ]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "bytes"].bytes, [1, 2, 3]) +// } +// +// func testJSONBSerialize() { +// struct Object: Codable, PostgresCodable { +// let foo: Int +// let bar: Int +// } +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow(try conn?.close().wait()) } +// do { +// var postgresData: PostgresData? +// XCTAssertNoThrow(postgresData = try PostgresData(jsonb: Object(foo: 1, bar: 2))) +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query("select $1::jsonb as jsonb", [XCTUnwrap(postgresData)]).wait()) +// +// var object: Object? +// XCTAssertNoThrow(object = try rows?.first?.decode(Object.self, context: .default)) +// XCTAssertEqual(object?.foo, 1) +// XCTAssertEqual(object?.bar, 2) +// } +// +// do { +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query("select jsonb_build_object('foo',1,'bar',2) as jsonb").wait()) +// +// var object: Object? +// XCTAssertNoThrow(object = try rows?.first?.decode(Object.self, context: .default)) +// XCTAssertEqual(object?.foo, 1) +// XCTAssertEqual(object?.bar, 2) +// } +// } +// +// func testSimpleQueryVersion() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: [PostgresRow]? +// XCTAssertNoThrow(rows = try conn?.simpleQuery("SELECT version()").wait()) +// XCTAssertEqual(rows?.count, 1) +// XCTAssertEqual(try rows?.first?.decode(String.self, context: .default).contains("PostgreSQL"), true) +// } +// +// func testSimpleQueryVersionUsingUDS() throws { +// try XCTSkipUnless(env("POSTGRES_SOCKET") != nil) +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.testUDS(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: [PostgresRow]? +// XCTAssertNoThrow(rows = try conn?.simpleQuery("SELECT version()").wait()) +// XCTAssertEqual(rows?.count, 1) +// XCTAssertEqual(try rows?.first?.decode(String.self, context: .default).contains("PostgreSQL"), true) +// } +// +// func testSimpleQueryVersionUsingEstablishedChannel() throws { +// let channel = try ClientBootstrap(group: self.group).connect(to: PostgresConnection.address()).wait() +// let conn = try PostgresConnection.testChannel(channel, on: self.eventLoop).wait() +// defer { XCTAssertNoThrow(try conn.close().wait()) } +// +// let rows = try conn.simpleQuery("SELECT version()").wait() +// XCTAssertEqual(rows.count, 1) +// XCTAssertEqual(try rows.first?.decode(String.self, context: .default).contains("PostgreSQL"), true) +// } +// +// func testQueryVersion() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query("SELECT version()", .init()).wait()) +// XCTAssertEqual(rows?.count, 1) +// XCTAssertEqual(try rows?.first?.decode(String.self, context: .default).contains("PostgreSQL"), true) +// } +// +// func testQuerySelectParameter() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query("SELECT $1::TEXT as foo", ["hello"]).wait()) +// XCTAssertEqual(rows?.count, 1) +// XCTAssertEqual(try rows?.first?.decode(String.self, context: .default), "hello") +// } +// +// func testSQLError() throws { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// XCTAssertThrowsError(_ = try conn?.simpleQuery("SELECT &").wait()) { error in +// XCTAssertEqual((error as? PostgresError)?.code, .syntaxError) +// } +// } +// +// func testNotificationsEmptyPayload() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// let receivedNotifications = ManagedAtomic<Int>(0) +// conn?.addListener(channel: "example") { context, notification in +// receivedNotifications.wrappingIncrement(ordering: .relaxed) +// XCTAssertEqual(notification.channel, "example") +// XCTAssertEqual(notification.payload, "") +// } +// XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) +// // Notifications are asynchronous, so we should run at least one more query to make sure we'll have received the notification response by then +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) +// XCTAssertEqual(receivedNotifications.load(ordering: .relaxed), 1) +// } +// +// func testNotificationsNonEmptyPayload() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// let receivedNotifications = ManagedAtomic<Int>(0) +// conn?.addListener(channel: "example") { context, notification in +// receivedNotifications.wrappingIncrement(ordering: .relaxed) +// XCTAssertEqual(notification.channel, "example") +// XCTAssertEqual(notification.payload, "Notification payload example") +// } +// XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example, 'Notification payload example'").wait()) +// // Notifications are asynchronous, so we should run at least one more query to make sure we'll have received the notification response by then +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) +// XCTAssertEqual(receivedNotifications.load(ordering: .relaxed), 1) +// } +// +// func testNotificationsRemoveHandlerWithinHandler() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// let receivedNotifications = ManagedAtomic<Int>(0) +// conn?.addListener(channel: "example") { context, notification in +// receivedNotifications.wrappingIncrement(ordering: .relaxed) +// context.stop() +// } +// XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) +// XCTAssertEqual(receivedNotifications.load(ordering: .relaxed), 1) +// } +// +// func testNotificationsRemoveHandlerOutsideHandler() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// let receivedNotifications = ManagedAtomic<Int>(0) +// let context = conn?.addListener(channel: "example") { context, notification in +// receivedNotifications.wrappingIncrement(ordering: .relaxed) +// } +// XCTAssertNotNil(context) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) +// context?.stop() +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) +// XCTAssertEqual(receivedNotifications.load(ordering: .relaxed), 1) +// } +// +// func testNotificationsMultipleRegisteredHandlers() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// let receivedNotifications1 = ManagedAtomic<Int>(0) +// conn?.addListener(channel: "example") { context, notification in +// receivedNotifications1.wrappingIncrement(ordering: .relaxed) +// } +// let receivedNotifications2 = ManagedAtomic<Int>(0) +// conn?.addListener(channel: "example") { context, notification in +// receivedNotifications2.wrappingIncrement(ordering: .relaxed) +// } +// XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) +// XCTAssertEqual(receivedNotifications1.load(ordering: .relaxed), 1) +// XCTAssertEqual(receivedNotifications2.load(ordering: .relaxed), 1) +// } +// +// func testNotificationsMultipleRegisteredHandlersRemoval() throws { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// let receivedNotifications1 = ManagedAtomic<Int>(0) +// XCTAssertNotNil(conn?.addListener(channel: "example") { context, notification in +// receivedNotifications1.wrappingIncrement(ordering: .relaxed) +// context.stop() +// }) +// let receivedNotifications2 = ManagedAtomic<Int>(0) +// XCTAssertNotNil(conn?.addListener(channel: "example") { context, notification in +// receivedNotifications2.wrappingIncrement(ordering: .relaxed) +// }) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) +// XCTAssertEqual(receivedNotifications1.load(ordering: .relaxed), 1) +// XCTAssertEqual(receivedNotifications2.load(ordering: .relaxed), 2) +// } +// +// func testNotificationHandlerFiltersOnChannel() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// XCTAssertNotNil(conn?.addListener(channel: "desired") { context, notification in +// XCTFail("Received notification on channel that handler was not registered for") +// }) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN undesired").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY undesired").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) +// } +// +// func testSelectTypes() throws { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var results: [PostgresRow]? +// XCTAssertNoThrow(results = try conn?.simpleQuery("SELECT * FROM pg_type").wait()) +// XCTAssert((results?.count ?? 0) > 350, "Results count not large enough") +// } +// +// func testSelectType() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var results: [PostgresRow]? +// XCTAssertNoThrow(results = try conn?.simpleQuery("SELECT * FROM pg_type WHERE typname = 'float8'").wait()) +// // [ +// // "typreceive": "float8recv", +// // "typelem": "0", +// // "typarray": "1022", +// // "typalign": "d", +// // "typanalyze": "-", +// // "typtypmod": "-1", +// // "typname": "float8", +// // "typnamespace": "11", +// // "typdefault": "<null>", +// // "typdefaultbin": "<null>", +// // "typcollation": "0", +// // "typispreferred": "t", +// // "typrelid": "0", +// // "typbyval": "t", +// // "typnotnull": "f", +// // "typinput": "float8in", +// // "typlen": "8", +// // "typcategory": "N", +// // "typowner": "10", +// // "typtype": "b", +// // "typdelim": ",", +// // "typndims": "0", +// // "typbasetype": "0", +// // "typacl": "<null>", +// // "typisdefined": "t", +// // "typmodout": "-", +// // "typmodin": "-", +// // "typsend": "float8send", +// // "typstorage": "p", +// // "typoutput": "float8out" +// // ] +// XCTAssertEqual(results?.count, 1) +// let row = results?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "typname"].string, "float8") +// XCTAssertEqual(row?[data: "typnamespace"].int, 11) +// XCTAssertEqual(row?[data: "typowner"].int, 10) +// XCTAssertEqual(row?[data: "typlen"].int, 8) +// } +// +// func testIntegers() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// struct Integers: Decodable { +// let smallint: Int16 +// let smallint_min: Int16 +// let smallint_max: Int16 +// let int: Int32 +// let int_min: Int32 +// let int_max: Int32 +// let bigint: Int64 +// let bigint_min: Int64 +// let bigint_max: Int64 +// } +// var results: PostgresQueryResult? +// XCTAssertNoThrow(results = try conn?.query(""" +// SELECT +// 1::SMALLINT as smallint, +// -32767::SMALLINT as smallint_min, +// 32767::SMALLINT as smallint_max, +// 1::INT as int, +// -2147483647::INT as int_min, +// 2147483647::INT as int_max, +// 1::BIGINT as bigint, +// -9223372036854775807::BIGINT as bigint_min, +// 9223372036854775807::BIGINT as bigint_max +// """).wait()) +// XCTAssertEqual(results?.count, 1) +// +// let row = results?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "smallint"].int16, 1) +// XCTAssertEqual(row?[data: "smallint_min"].int16, -32_767) +// XCTAssertEqual(row?[data: "smallint_max"].int16, 32_767) +// XCTAssertEqual(row?[data: "int"].int32, 1) +// XCTAssertEqual(row?[data: "int_min"].int32, -2_147_483_647) +// XCTAssertEqual(row?[data: "int_max"].int32, 2_147_483_647) +// XCTAssertEqual(row?[data: "bigint"].int64, 1) +// XCTAssertEqual(row?[data: "bigint_min"].int64, -9_223_372_036_854_775_807) +// XCTAssertEqual(row?[data: "bigint_max"].int64, 9_223_372_036_854_775_807) +// } +// +// func testPi() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// struct Pi: Decodable { +// let text: String +// let numeric_string: String +// let numeric_decimal: Decimal +// let double: Double +// let float: Float +// } +// var results: PostgresQueryResult? +// XCTAssertNoThrow(results = try conn?.query(""" +// SELECT +// pi()::TEXT as text, +// pi()::NUMERIC as numeric_string, +// pi()::NUMERIC as numeric_decimal, +// pi()::FLOAT8 as double, +// pi()::FLOAT4 as float +// """).wait()) +// XCTAssertEqual(results?.count, 1) +// let row = results?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "text"].string?.hasPrefix("3.14159265"), true) +// XCTAssertEqual(row?[data: "numeric_string"].string?.hasPrefix("3.14159265"), true) +// XCTAssertTrue(row?[data: "numeric_decimal"].decimal?.isLess(than: 3.14159265358980) ?? false) +// XCTAssertFalse(row?[data: "numeric_decimal"].decimal?.isLess(than: 3.14159265358978) ?? true) +// XCTAssertTrue(row?[data: "double"].double?.description.hasPrefix("3.141592") ?? false) +// XCTAssertTrue(row?[data: "float"].float?.description.hasPrefix("3.141592") ?? false) +// } +// +// func testUUID() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// struct Model: Decodable { +// let id: UUID +// let string: String +// } +// var results: PostgresQueryResult? +// XCTAssertNoThrow(results = try conn?.query(""" +// SELECT +// '123e4567-e89b-12d3-a456-426655440000'::UUID as id, +// '123e4567-e89b-12d3-a456-426655440000'::UUID as string +// """).wait()) +// XCTAssertEqual(results?.count, 1) +// let row = results?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "id"].uuid, UUID(uuidString: "123E4567-E89B-12D3-A456-426655440000")) +// XCTAssertEqual(UUID(uuidString: row?[data: "id"].string ?? ""), UUID(uuidString: "123E4567-E89B-12D3-A456-426655440000")) +// } +// +// func testInt4Range() async throws { +// let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get() +// self.addTeardownBlock { +// try await conn.close() +// } +// struct Model: Decodable { +// let range: Range<Int32> +// } +// let results1: PostgresQueryResult = try await conn.query(""" +// SELECT +// '[\(Int32.min), \(Int32.max))'::int4range AS range +// """).get() +// XCTAssertEqual(results1.count, 1) +// var row = results1.first?.makeRandomAccess() +// let expectedRange: Range<Int32> = Int32.min..<Int32.max +// let decodedRange = try row?.decode(column: "range", as: Range<Int32>.self, context: .default) +// XCTAssertEqual(decodedRange, expectedRange) +// +// let results2 = try await conn.query(""" +// SELECT +// ARRAY[ +// '[0, 1)'::int4range, +// '[10, 11)'::int4range +// ] AS ranges +// """).get() +// XCTAssertEqual(results2.count, 1) +// row = results2.first?.makeRandomAccess() +// let decodedRangeArray = try row?.decode(column: "ranges", as: [Range<Int32>].self, context: .default) +// let decodedClosedRangeArray = try row?.decode(column: "ranges", as: [ClosedRange<Int32>].self, context: .default) +// XCTAssertEqual(decodedRangeArray, [0..<1, 10..<11]) +// XCTAssertEqual(decodedClosedRangeArray, [0...0, 10...10]) +// } +// +// func testEmptyInt4Range() async throws { +// let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get() +// self.addTeardownBlock { +// try await conn.close() +// } +// struct Model: Decodable { +// let range: Range<Int32> +// } +// let randomValue = Int32.random(in: Int32.min...Int32.max) +// let results: PostgresQueryResult = try await conn.query(""" +// SELECT +// '[\(randomValue),\(randomValue))'::int4range AS range +// """).get() +// XCTAssertEqual(results.count, 1) +// let row = results.first?.makeRandomAccess() +// let expectedRange: Range<Int32> = Int32.valueForEmptyRange..<Int32.valueForEmptyRange +// let decodedRange = try row?.decode(column: "range", as: Range<Int32>.self, context: .default) +// XCTAssertEqual(decodedRange, expectedRange) +// +// XCTAssertThrowsError( +// try row?.decode(column: "range", as: ClosedRange<Int32>.self, context: .default) +// ) +// } +// +// func testInt8Range() async throws { +// let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get() +// self.addTeardownBlock { +// try await conn.close() +// } +// struct Model: Decodable { +// let range: Range<Int64> +// } +// let results1: PostgresQueryResult = try await conn.query(""" +// SELECT +// '[\(Int64.min), \(Int64.max))'::int8range AS range +// """).get() +// XCTAssertEqual(results1.count, 1) +// var row = results1.first?.makeRandomAccess() +// let expectedRange: Range<Int64> = Int64.min..<Int64.max +// let decodedRange = try row?.decode(column: "range", as: Range<Int64>.self, context: .default) +// XCTAssertEqual(decodedRange, expectedRange) +// +// let results2: PostgresQueryResult = try await conn.query(""" +// SELECT +// ARRAY[ +// '[0, 1)'::int8range, +// '[10, 11)'::int8range +// ] AS ranges +// """).get() +// XCTAssertEqual(results2.count, 1) +// row = results2.first?.makeRandomAccess() +// let decodedRangeArray = try row?.decode(column: "ranges", as: [Range<Int64>].self, context: .default) +// let decodedClosedRangeArray = try row?.decode(column: "ranges", as: [ClosedRange<Int64>].self, context: .default) +// XCTAssertEqual(decodedRangeArray, [0..<1, 10..<11]) +// XCTAssertEqual(decodedClosedRangeArray, [0...0, 10...10]) +// } +// +// func testEmptyInt8Range() async throws { +// let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get() +// self.addTeardownBlock { +// try await conn.close() +// } +// struct Model: Decodable { +// let range: Range<Int64> +// } +// let randomValue = Int64.random(in: Int64.min...Int64.max) +// let results: PostgresQueryResult = try await conn.query(""" +// SELECT +// '[\(randomValue),\(randomValue))'::int8range AS range +// """).get() +// XCTAssertEqual(results.count, 1) +// let row = results.first?.makeRandomAccess() +// let expectedRange: Range<Int64> = Int64.valueForEmptyRange..<Int64.valueForEmptyRange +// let decodedRange = try row?.decode(column: "range", as: Range<Int64>.self, context: .default) +// XCTAssertEqual(decodedRange, expectedRange) +// +// XCTAssertThrowsError( +// try row?.decode(column: "range", as: ClosedRange<Int64>.self, context: .default) +// ) +// } +// +// func testDates() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// struct Dates: Decodable { +// var date: Date +// var timestamp: Date +// var timestamptz: Date +// } +// var results: PostgresQueryResult? +// XCTAssertNoThrow(results = try conn?.query(""" +// SELECT +// '2016-01-18 01:02:03 +0042'::DATE as date, +// '2016-01-18 01:02:03 +0042'::TIMESTAMP as timestamp, +// '2016-01-18 01:02:03 +0042'::TIMESTAMPTZ as timestamptz +// """).wait()) +// XCTAssertEqual(results?.count, 1) +// let row = results?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "date"].date?.description, "2016-01-18 00:00:00 +0000") +// XCTAssertEqual(row?[data: "timestamp"].date?.description, "2016-01-18 01:02:03 +0000") +// XCTAssertEqual(row?[data: "timestamptz"].date?.description, "2016-01-18 00:20:03 +0000") +// } +// +// /// https://github.com/vapor/nio-postgres/issues/20 +// func testBindInteger() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// XCTAssertNoThrow(_ = try conn?.simpleQuery("drop table if exists person;").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("create table person(id serial primary key, first_name text, last_name text);").wait()) +// defer { XCTAssertNoThrow(_ = try conn?.simpleQuery("drop table person;").wait()) } +// let id = PostgresData(int32: 5) +// XCTAssertNoThrow(_ = try conn?.query("SELECT id, first_name, last_name FROM person WHERE id = $1", [id]).wait()) +// } +// +// // https://github.com/vapor/nio-postgres/issues/21 +// func testAverageLengthNumeric() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var results: PostgresQueryResult? +// XCTAssertNoThrow(results = try conn?.query("select avg(length('foo')) as average_length").wait()) +// let row = results?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: 0].double, 3.0) +// } +// +// func testNumericParsing() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// '1234.5678'::numeric as a, +// '-123.456'::numeric as b, +// '123456.789123'::numeric as c, +// '3.14159265358979'::numeric as d, +// '10000'::numeric as e, +// '0.00001'::numeric as f, +// '100000000'::numeric as g, +// '0.000000001'::numeric as h, +// '100000000000'::numeric as i, +// '0.000000000001'::numeric as j, +// '123000000000'::numeric as k, +// '0.000000000123'::numeric as l, +// '0.5'::numeric as m +// """).wait()) +// XCTAssertEqual(rows?.count, 1) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "a"].string, "1234.5678") +// XCTAssertEqual(row?[data: "b"].string, "-123.456") +// XCTAssertEqual(row?[data: "c"].string, "123456.789123") +// XCTAssertEqual(row?[data: "d"].string, "3.14159265358979") +// XCTAssertEqual(row?[data: "e"].string, "10000") +// XCTAssertEqual(row?[data: "f"].string, "0.00001") +// XCTAssertEqual(row?[data: "g"].string, "100000000") +// XCTAssertEqual(row?[data: "h"].string, "0.000000001") +// XCTAssertEqual(row?[data: "k"].string, "123000000000") +// XCTAssertEqual(row?[data: "l"].string, "0.000000000123") +// XCTAssertEqual(row?[data: "m"].string, "0.5") +// } +// +// func testSingleNumericParsing() { +// // this seemingly duped test is useful for debugging numeric parsing +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// let numeric = "790226039477542363.6032384900176272473" +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// '\(numeric)'::numeric as n +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "n"].string, numeric) +// } +// +// func testRandomlyGeneratedNumericParsing() throws { +// // this test takes a long time to run +// try XCTSkipUnless(Self.shouldRunLongRunningTests) +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// for _ in 0..<1_000_000 { +// let integer = UInt.random(in: UInt.min..<UInt.max) +// let fraction = UInt.random(in: UInt.min..<UInt.max) +// let number = "\(integer).\(fraction)" +// .trimmingCharacters(in: CharacterSet(["0"])) +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// '\(number)'::numeric as n +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "n"].string, number) +// } +// } +// +// func testNumericSerialization() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// let a = PostgresNumeric(string: "123456.789123")! +// let b = PostgresNumeric(string: "-123456.789123")! +// let c = PostgresNumeric(string: "3.14159265358979")! +// let d = PostgresNumeric(string: "1234567898765")! +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// $1::numeric as a, +// $2::numeric as b, +// $3::numeric as c, +// $4::numeric as d +// """, [ +// .init(numeric: a), +// .init(numeric: b), +// .init(numeric: c), +// .init(numeric: d) +// ]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "a"].decimal, Decimal(string: "123456.789123")!) +// XCTAssertEqual(row?[data: "b"].decimal, Decimal(string: "-123456.789123")!) +// XCTAssertEqual(row?[data: "c"].decimal, Decimal(string: "3.14159265358979")!) +// XCTAssertEqual(row?[data: "d"].decimal, Decimal(string: "1234567898765")!) +// } +// +// func testDecimalStringSerialization() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE IF EXISTS \"table1\"").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery(""" +// CREATE TABLE table1 ( +// "balance" text NOT NULL +// ); +// """).wait()) +// defer { XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE \"table1\"").wait()) } +// +// XCTAssertNoThrow(_ = try conn?.query("INSERT INTO table1 VALUES ($1)", [.init(decimal: Decimal(string: "123456.789123")!)]).wait()) +// +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// SELECT +// "balance" +// FROM table1 +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "balance"].decimal, Decimal(string: "123456.789123")!) +// } +// +// func testMoney() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// '0'::money as a, +// '0.05'::money as b, +// '0.23'::money as c, +// '3.14'::money as d, +// '12345678.90'::money as e +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "a"].string, "0.00") +// XCTAssertEqual(row?[data: "b"].string, "0.05") +// XCTAssertEqual(row?[data: "c"].string, "0.23") +// XCTAssertEqual(row?[data: "d"].string, "3.14") +// XCTAssertEqual(row?[data: "e"].string, "12345678.90") +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testIntegerArrayParse() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// '{1,2,3}'::int[] as array +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "array"].array(of: Int.self), [1, 2, 3]) +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testEmptyIntegerArrayParse() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// '{}'::int[] as array +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "array"].array(of: Int.self), []) +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testOptionalIntegerArrayParse() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// '{1, 2, NULL, 4}'::int8[] as array +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "array"].array(of: Int?.self), [1, 2, nil, 4]) +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testNullIntegerArrayParse() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// null::int[] as array +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "array"].array(of: Int.self), nil) +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testIntegerArraySerialize() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// $1::int8[] as array +// """, [ +// PostgresData(array: [1, 2, 3]) +// ]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "array"].array(of: Int.self), [1, 2, 3]) +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testEmptyIntegerArraySerialize() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// $1::int8[] as array +// """, [ +// PostgresData(array: [] as [Int]) +// ]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "array"].array(of: Int.self), []) +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testOptionalIntegerArraySerialize() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// $1::int8[] as array +// """, [ +// PostgresData(array: [1, nil, 3] as [Int64?]) +// ]).wait()) +// XCTAssertEqual(rows?.count, 1) +// let row = rows?.first?.makeRandomAccess() +// 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? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// XCTAssertNoThrow(_ = try conn?.simpleQuery(#"DROP TABLE IF EXISTS "non_null_empty_strings""#).wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery(""" +// CREATE TABLE non_null_empty_strings ( +// "id" SERIAL, +// "nonNullString" text NOT NULL, +// PRIMARY KEY ("id") +// ); +// """).wait()) +// defer { XCTAssertNoThrow(_ = try conn?.simpleQuery(#"DROP TABLE "non_null_empty_strings""#).wait()) } +// +// XCTAssertNoThrow(_ = try conn?.simpleQuery(""" +// INSERT INTO non_null_empty_strings ("nonNullString") VALUES ('') +// """).wait()) +// +// var rows: [PostgresRow]? +// XCTAssertNoThrow(rows = try conn?.simpleQuery(#"SELECT * FROM "non_null_empty_strings""#).wait()) +// XCTAssertEqual(rows?.count, 1) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "nonNullString"].string, "") // <--- this fails +// } +// +// +// func testBoolSerialize() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// do { +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query("select $1::bool as bool", [true]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "bool"].bool, true) +// } +// do { +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query("select $1::bool as bool", [false]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "bool"].bool, false) +// } +// do { +// var rows: [PostgresRow]? +// XCTAssertNoThrow(rows = try conn?.simpleQuery("select true::bool as bool").wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "bool"].bool, true) +// } +// do { +// var rows: [PostgresRow]? +// XCTAssertNoThrow(rows = try conn?.simpleQuery("select false::bool as bool").wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "bool"].bool, false) +// } +// } +// +// func testBytesSerialize() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query("select $1::bytea as bytes", [ +// PostgresData(bytes: [1, 2, 3]) +// ]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "bytes"].bytes, [1, 2, 3]) +// } +// +// func testJSONBSerialize() { +// struct Object: Codable, PostgresCodable { +// let foo: Int +// let bar: Int +// } +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow(try conn?.close().wait()) } +// do { +// var postgresData: PostgresData? +// XCTAssertNoThrow(postgresData = try PostgresData(jsonb: Object(foo: 1, bar: 2))) +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query("select $1::jsonb as jsonb", [XCTUnwrap(postgresData)]).wait()) +// +// var object: Object? +// XCTAssertNoThrow(object = try rows?.first?.decode(Object.self, context: .default)) +// XCTAssertEqual(object?.foo, 1) +// XCTAssertEqual(object?.bar, 2) +// } +// +// do { +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query("select jsonb_build_object('foo',1,'bar',2) as jsonb").wait()) +// +// var object: Object? +// XCTAssertNoThrow(object = try rows?.first?.decode(Object.self, context: .default)) +// XCTAssertEqual(object?.foo, 1) +// XCTAssertEqual(object?.bar, 2) +// } +// } +// +// func testInt4RangeSerialize() async throws { +// let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get() +// self.addTeardownBlock { +// try await conn.close() +// } +// do { +// let range: Range<Int32> = Int32.min..<Int32.max +// var binds = PostgresBindings() +// binds.append(range, context: .default) +// let query = PostgresQuery( +// unsafeSQL: "select $1::int4range as range", +// binds: binds +// ) +// let rowSequence: PostgresRowSequence? = try await conn.query(query, logger: .psqlTest) +// var rowIterator: PostgresRowSequence.AsyncIterator? = rowSequence?.makeAsyncIterator() +// let row: PostgresRow? = try await rowIterator?.next() +// let decodedRange: Range<Int32>? = try row?.decode(Range<Int32>.self, context: .default) +// XCTAssertEqual(range, decodedRange) +// } +// do { +// let emptyRange: Range<Int32> = Int32.min..<Int32.min +// var binds = PostgresBindings() +// binds.append(emptyRange, context: .default) +// let query = PostgresQuery( +// unsafeSQL: "select $1::int4range as range", +// binds: binds +// ) +// let rowSequence: PostgresRowSequence? = try await conn.query(query, logger: .psqlTest) +// var rowIterator: PostgresRowSequence.AsyncIterator? = rowSequence?.makeAsyncIterator() +// let row: PostgresRow? = try await rowIterator?.next() +// let decodedEmptyRange: Range<Int32>? = try row?.decode(Range<Int32>.self, context: .default) +// let expectedRange: Range<Int32> = Int32.valueForEmptyRange..<Int32.valueForEmptyRange +// XCTAssertNotEqual(emptyRange, expectedRange) +// XCTAssertEqual(expectedRange, decodedEmptyRange) +// } +// do { +// let closedRange: ClosedRange<Int32> = Int32.min...(Int32.max - 1) +// var binds = PostgresBindings() +// binds.append(closedRange, context: .default) +// let query = PostgresQuery( +// unsafeSQL: "select $1::int4range as range", +// binds: binds +// ) +// let rowSequence: PostgresRowSequence? = try await conn.query(query, logger: .psqlTest) +// var rowIterator: PostgresRowSequence.AsyncIterator? = rowSequence?.makeAsyncIterator() +// let row: PostgresRow? = try await rowIterator?.next() +// let decodedClosedRange: ClosedRange<Int32>? = try row?.decode(ClosedRange<Int32>.self, context: .default) +// XCTAssertEqual(closedRange, decodedClosedRange) +// } +// } +// +// func testInt8RangeSerialize() async throws { +// let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get() +// self.addTeardownBlock { +// try await conn.close() +// } +// do { +// let range: Range<Int64> = Int64.min..<Int64.max +// var binds = PostgresBindings() +// binds.append(range, context: .default) +// let query = PostgresQuery( +// unsafeSQL: "select $1::int8range as range", +// binds: binds +// ) +// let rowSequence: PostgresRowSequence? = try await conn.query(query, logger: .psqlTest) +// var rowIterator: PostgresRowSequence.AsyncIterator? = rowSequence?.makeAsyncIterator() +// let row: PostgresRow? = try await rowIterator?.next() +// let decodedRange: Range<Int64>? = try row?.decode(Range<Int64>.self, context: .default) +// XCTAssertEqual(range, decodedRange) +// } +// do { +// let emptyRange: Range<Int64> = Int64.min..<Int64.min +// var binds = PostgresBindings() +// binds.append(emptyRange, context: .default) +// let query = PostgresQuery( +// unsafeSQL: "select $1::int8range as range", +// binds: binds +// ) +// let rowSequence: PostgresRowSequence? = try await conn.query(query, logger: .psqlTest) +// var rowIterator: PostgresRowSequence.AsyncIterator? = rowSequence?.makeAsyncIterator() +// let row: PostgresRow? = try await rowIterator?.next() +// let decodedEmptyRange: Range<Int64>? = try row?.decode(Range<Int64>.self, context: .default) +// let expectedRange: Range<Int64> = Int64.valueForEmptyRange..<Int64.valueForEmptyRange +// XCTAssertNotEqual(emptyRange, expectedRange) +// XCTAssertEqual(expectedRange, decodedEmptyRange) +// } +// do { +// let closedRange: ClosedRange<Int64> = Int64.min...(Int64.max - 1) +// var binds = PostgresBindings() +// binds.append(closedRange, context: .default) +// let query = PostgresQuery( +// unsafeSQL: "select $1::int8range as range", +// binds: binds +// ) +// let rowSequence: PostgresRowSequence? = try await conn.query(query, logger: .psqlTest) +// var rowIterator: PostgresRowSequence.AsyncIterator? = rowSequence?.makeAsyncIterator() +// let row: PostgresRow? = try await rowIterator?.next() +// let decodedClosedRange: ClosedRange<Int64>? = try row?.decode(ClosedRange<Int64>.self, context: .default) +// XCTAssertEqual(closedRange, decodedClosedRange) +// } +// } +// +// func testRemoteTLSServer() { +// // postgres://uymgphwj:7_tHbREdRwkqAdu4KoIS7hQnNxr8J1LA@elmer.db.elephantsql.com:5432/uymgphwj +// var conn: PostgresConnection? +// let logger = Logger(label: "test") +// let sslContext = try! NIOSSLContext(configuration: .makeClientConfiguration()) +// let config = PostgresConnection.Configuration( +// host: "elmer.db.elephantsql.com", +// port: 5432, +// username: "uymgphwj", +// password: "7_tHbREdRwkqAdu4KoIS7hQnNxr8J1LA", +// database: "uymgphwj", +// tls: .require(sslContext) +// ) +// XCTAssertNoThrow(conn = try PostgresConnection.connect(on: eventLoop, configuration: config, id: 0, logger: logger).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: [PostgresRow]? +// XCTAssertNoThrow(rows = try conn?.simpleQuery("SELECT version()").wait()) +// XCTAssertEqual(rows?.count, 1) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "version"].string?.contains("PostgreSQL"), true) +// } +// +// @available(*, deprecated, message: "Test deprecated functionality") +// func testFailingTLSConnectionClosesConnection() { +// // There was a bug (https://github.com/vapor/postgres-nio/issues/133) where we would hit +// // an assert because we didn't close the connection. This test should succeed without hitting +// // the assert +// +// // postgres://uymgphwj:7_tHbREdRwkqAdu4KoIS7hQnNxr8J1LA@elmer.db.elephantsql.com:5432/uymgphwj +// +// // We should get an error because you can't use an IP address for SNI, but we shouldn't bomb out by +// // hitting the assert +// XCTAssertThrowsError( +// try PostgresConnection.connect( +// to: SocketAddress.makeAddressResolvingHost("elmer.db.elephantsql.com", port: 5432), +// tlsConfiguration: .makeClientConfiguration(), +// serverHostname: "34.228.73.168", +// on: eventLoop +// ).wait() +// ) +// // If we hit this, we're all good +// XCTAssertTrue(true) +// } +// +// @available(*, deprecated, message: "Test deprecated functionality") +// func testInvalidPassword() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.testUnauthenticated(on: eventLoop).wait()) +// let authFuture = conn?.authenticate(username: "invalid", database: "invalid", password: "bad") +// XCTAssertThrowsError(_ = try authFuture?.wait()) { error in +// XCTAssert((error as? PostgresError)?.code == .invalidPassword || (error as? PostgresError)?.code == .invalidAuthorizationSpecification) +// } +// +// // in this case the connection will be closed by the remote +// XCTAssertNoThrow(try conn?.closeFuture.wait()) +// } +// +// func testColumnsInJoin() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// let dateInTable1 = Date(timeIntervalSince1970: 1234) +// let dateInTable2 = Date(timeIntervalSince1970: 5678) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE IF EXISTS \"table1\"").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery(""" +// CREATE TABLE table1 ( +// "id" int8 NOT NULL, +// "table2_id" int8, +// "intValue" int8, +// "stringValue" text, +// "dateValue" timestamptz, +// PRIMARY KEY ("id") +// ); +// """).wait()) +// defer { XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE \"table1\"").wait()) } +// +// XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE IF EXISTS \"table2\"").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery(""" +// CREATE TABLE table2 ( +// "id" int8 NOT NULL, +// "intValue" int8, +// "stringValue" text, +// "dateValue" timestamptz, +// PRIMARY KEY ("id") +// ); +// """).wait()) +// defer { XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE \"table2\"").wait()) } +// +// XCTAssertNoThrow(_ = try conn?.simpleQuery("INSERT INTO table1 VALUES (12, 34, 56, 'stringInTable1', to_timestamp(1234))").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("INSERT INTO table2 VALUES (34, 78, 'stringInTable2', to_timestamp(5678))").wait()) +// +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// SELECT +// "table1"."id" as "t1_id", +// "table1"."intValue" as "t1_intValue", +// "table1"."dateValue" as "t1_dateValue", +// "table1"."stringValue" as "t1_stringValue", +// "table2"."id" as "t2_id", +// "table2"."intValue" as "t2_intValue", +// "table2"."dateValue" as "t2_dateValue", +// "table2"."stringValue" as "t2_stringValue", +// * +// FROM table1 INNER JOIN table2 ON table1.table2_id = table2.id +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "t1_id"].int, 12) +// XCTAssertEqual(row?[data: "table2_id"].int, 34) +// XCTAssertEqual(row?[data: "t1_intValue"].int, 56) +// XCTAssertEqual(row?[data: "t1_stringValue"].string, "stringInTable1") +// XCTAssertEqual(row?[data: "t1_dateValue"].date, dateInTable1) +// XCTAssertEqual(row?[data: "t2_id"].int, 34) +// XCTAssertEqual(row?[data: "t2_intValue"].int, 78) +// XCTAssertEqual(row?[data: "t2_stringValue"].string, "stringInTable2") +// XCTAssertEqual(row?[data: "t2_dateValue"].date, dateInTable2) +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testStringArrays() { +// let query = """ +// SELECT +// $1::uuid as "id", +// $2::bigint as "revision", +// $3::timestamp as "updated_at", +// $4::timestamp as "created_at", +// $5::text as "name", +// $6::text[] as "countries", +// $7::text[] as "languages", +// $8::text[] as "currencies" +// """ +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(query, [ +// PostgresData(uuid: UUID(uuidString: "D2710E16-EB07-4FD6-A87E-B1BE41C9BD3D")!), +// PostgresData(int: Int(0)), +// PostgresData(date: Date(timeIntervalSince1970: 0)), +// PostgresData(date: Date(timeIntervalSince1970: 0)), +// PostgresData(string: "Foo"), +// PostgresData(array: ["US"]), +// PostgresData(array: ["en"]), +// PostgresData(array: ["USD", "DKK"]), +// ]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "countries"].array(of: String.self), ["US"]) +// XCTAssertEqual(row?[data: "languages"].array(of: String.self), ["en"]) +// XCTAssertEqual(row?[data: "currencies"].array(of: String.self), ["USD", "DKK"]) +// } +// +// func testBindDate() { +// // https://github.com/vapor/postgres-nio/issues/53 +// let date = Date(timeIntervalSince1970: 1571425782) +// let query = """ +// SELECT $1::json as "date" +// """ +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// XCTAssertThrowsError(_ = try conn?.query(query, [.init(date: date)]).wait()) { error in +// guard let postgresError = try? XCTUnwrap(error as? PostgresError) else { return } +// guard case let .server(serverError) = postgresError else { +// XCTFail("Expected a .serverError but got \(postgresError)") +// return +// } +// XCTAssertEqual(serverError.fields[.routine], "transformTypeCast") +// } +// +// } +// +// func testBindCharString() { +// // https://github.com/vapor/postgres-nio/issues/53 +// let query = """ +// SELECT $1::char as "char" +// """ +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(query, [.init(string: "f")]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "char"].string, "f") +// } +// +// func testBindCharUInt8() { +// // https://github.com/vapor/postgres-nio/issues/53 +// let query = """ +// SELECT $1::char as "char" +// """ +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(query, [.init(uint8: 42)]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "char"].string, "*") +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testDoubleArraySerialization() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// let doubles: [Double] = [3.14, 42] +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// $1::double precision[] as doubles +// """, [ +// .init(array: doubles) +// ]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "doubles"].array(of: Double.self), doubles) +// } +// +// // https://github.com/vapor/postgres-nio/issues/42 +// func testUInt8Serialization() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// $1::"char" as int +// """, [ +// .init(uint8: 5) +// ]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "int"].uint8, 5) +// } +// +// func testPreparedQuery() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var prepared: PreparedQuery? +// XCTAssertNoThrow(prepared = try conn?.prepare(query: "SELECT 1 as one;").wait()) +// var rows: [PostgresRow]? +// XCTAssertNoThrow(rows = try prepared?.execute().wait()) +// +// XCTAssertEqual(rows?.count, 1) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "one"].int, 1) +// } +// +// func testPrepareQueryClosure() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var queries: [[PostgresRow]]? +// XCTAssertNoThrow(queries = try conn?.prepare(query: "SELECT $1::text as foo;", handler: { query in +// let a = query.execute(["a"]) +// let b = query.execute(["b"]) +// let c = query.execute(["c"]) +// return EventLoopFuture.whenAllSucceed([a, b, c], on: self.eventLoop) +// }).wait()) +// XCTAssertEqual(queries?.count, 3) +// var resultIterator = queries?.makeIterator() +// XCTAssertEqual(try resultIterator?.next()?.first?.decode(String.self, context: .default), "a") +// XCTAssertEqual(try resultIterator?.next()?.first?.decode(String.self, context: .default), "b") +// XCTAssertEqual(try resultIterator?.next()?.first?.decode(String.self, context: .default), "c") +// } +// +// // https://github.com/vapor/postgres-nio/issues/122 +// func testPreparedQueryNoResults() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE IF EXISTS \"table_no_results\"").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery(""" +// CREATE TABLE table_no_results ( +// "id" int8 NOT NULL, +// "stringValue" text, +// PRIMARY KEY ("id") +// ); +// """).wait()) +// defer { XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE \"table_no_results\"").wait()) } +// +// XCTAssertNoThrow(_ = try conn?.prepare(query: "DELETE FROM \"table_no_results\" WHERE id = $1").wait()) +// } +// +// +// // https://github.com/vapor/postgres-nio/issues/71 +// func testChar1Serialization() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// '5'::char(1) as one, +// '5'::char(2) as two +// """).wait()) +// +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "one"].uint8, 53) +// XCTAssertEqual(row?[data: "one"].int16, 53) +// XCTAssertEqual(row?[data: "one"].string, "5") +// XCTAssertEqual(row?[data: "two"].uint8, nil) +// XCTAssertEqual(row?[data: "two"].int16, nil) +// XCTAssertEqual(row?[data: "two"].string, "5 ") +// } +// +// func testUserDefinedType() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// XCTAssertNoThrow(_ = try conn?.query("DROP TYPE IF EXISTS foo").wait()) +// XCTAssertNoThrow(_ = try conn?.query("CREATE TYPE foo AS ENUM ('bar', 'qux')").wait()) +// defer { +// XCTAssertNoThrow(_ = try conn?.query("DROP TYPE foo").wait()) +// } +// var res: PostgresQueryResult? +// XCTAssertNoThrow(res = try conn?.query("SELECT 'qux'::foo as foo").wait()) +// let row = res?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "foo"].string, "qux") +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testNullBind() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// var res: PostgresQueryResult? +// XCTAssertNoThrow(res = try conn?.query("SELECT $1::text as foo", [String?.none.postgresData!]).wait()) +// let row = res?.first?.makeRandomAccess() +// XCTAssertNil(row?[data: "foo"].string) +// } +// +// func testUpdateMetadata() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE IF EXISTS test_table").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("CREATE TABLE test_table(pk int PRIMARY KEY)").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("INSERT INTO test_table VALUES(1)").wait()) +// XCTAssertNoThrow(try conn?.query("DELETE FROM test_table", onMetadata: { metadata in +// XCTAssertEqual(metadata.command, "DELETE") +// XCTAssertEqual(metadata.oid, nil) +// XCTAssertEqual(metadata.rows, 1) +// }, onRow: { _ in }).wait()) +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query("DELETE FROM test_table").wait()) +// XCTAssertEqual(rows?.metadata.command, "DELETE") +// XCTAssertEqual(rows?.metadata.oid, nil) +// XCTAssertEqual(rows?.metadata.rows, 0) +// } +// +// func testTooManyBinds() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// let binds = [PostgresData].init(repeating: .null, count: Int(UInt16.max) + 1) +// XCTAssertThrowsError(try conn?.query("SELECT version()", binds).wait()) { error in +// guard case .tooManyParameters = (error as? PSQLError)?.code.base else { +// return XCTFail("Unexpected error: \(error)") +// } +// } +// } +// +// func testRemoteClose() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// XCTAssertNoThrow( try conn?.channel.close().wait() ) +// } +// +// // https://github.com/vapor/postgres-nio/issues/113 +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testVaryingCharArray() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// var res: PostgresQueryResult? +// XCTAssertNoThrow(res = try conn?.query(#"SELECT '{"foo", "bar", "baz"}'::VARCHAR[] as foo"#).wait()) +// let row = res?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "foo"].array(of: String.self), ["foo", "bar", "baz"]) +// } +// +// // https://github.com/vapor/postgres-nio/issues/115 +// func testSetTimeZone() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SET TIME ZONE INTERVAL '+5:45' HOUR TO MINUTE").wait()) +// XCTAssertNoThrow(_ = try conn?.query("SET TIME ZONE INTERVAL '+5:45' HOUR TO MINUTE").wait()) +// } +// +// func testIntegerConversions() throws { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// 'a'::char as test8, +// +// '-32768'::smallint as min16, +// '32767'::smallint as max16, +// +// '-2147483648'::integer as min32, +// '2147483647'::integer as max32, +// +// '-9223372036854775808'::bigint as min64, +// '9223372036854775807'::bigint as max64 +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "test8"].uint8, 97) +// XCTAssertEqual(row?[data: "test8"].int16, 97) +// XCTAssertEqual(row?[data: "test8"].int32, 97) +// XCTAssertEqual(row?[data: "test8"].int64, 97) +// +// XCTAssertEqual(row?[data: "min16"].uint8, nil) +// XCTAssertEqual(row?[data: "max16"].uint8, nil) +// XCTAssertEqual(row?[data: "min16"].int16, .min) +// XCTAssertEqual(row?[data: "max16"].int16, .max) +// XCTAssertEqual(row?[data: "min16"].int32, -32768) +// XCTAssertEqual(row?[data: "max16"].int32, 32767) +// XCTAssertEqual(row?[data: "min16"].int64, -32768) +// XCTAssertEqual(row?[data: "max16"].int64, 32767) +// +// XCTAssertEqual(row?[data: "min32"].uint8, nil) +// XCTAssertEqual(row?[data: "max32"].uint8, nil) +// XCTAssertEqual(row?[data: "min32"].int16, nil) +// XCTAssertEqual(row?[data: "max32"].int16, nil) +// XCTAssertEqual(row?[data: "min32"].int32, .min) +// XCTAssertEqual(row?[data: "max32"].int32, .max) +// XCTAssertEqual(row?[data: "min32"].int64, -2147483648) +// XCTAssertEqual(row?[data: "max32"].int64, 2147483647) +// +// XCTAssertEqual(row?[data: "min64"].uint8, nil) +// XCTAssertEqual(row?[data: "max64"].uint8, nil) +// XCTAssertEqual(row?[data: "min64"].int16, nil) +// XCTAssertEqual(row?[data: "max64"].int16, nil) +// XCTAssertEqual(row?[data: "min64"].int32, nil) +// XCTAssertEqual(row?[data: "max64"].int32, nil) +// XCTAssertEqual(row?[data: "min64"].int64, .min) +// XCTAssertEqual(row?[data: "max64"].int64, .max) +// } +// +// func testRemoteTLSServer() { +// // postgres://uymgphwj:7_tHbREdRwkqAdu4KoIS7hQnNxr8J1LA@elmer.db.elephantsql.com:5432/uymgphwj +// var conn: PostgresConnection? +// let logger = Logger(label: "test") +// let sslContext = try! NIOSSLContext(configuration: .makeClientConfiguration()) +// let config = PostgresConnection.Configuration( +// host: "elmer.db.elephantsql.com", +// port: 5432, +// username: "uymgphwj", +// password: "7_tHbREdRwkqAdu4KoIS7hQnNxr8J1LA", +// database: "uymgphwj", +// tls: .require(sslContext) +// ) +// XCTAssertNoThrow(conn = try PostgresConnection.connect(on: eventLoop, configuration: config, id: 0, logger: logger).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: [PostgresRow]? +// XCTAssertNoThrow(rows = try conn?.simpleQuery("SELECT version()").wait()) +// XCTAssertEqual(rows?.count, 1) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "version"].string?.contains("PostgreSQL"), true) +// } +// +// @available(*, deprecated, message: "Test deprecated functionality") +// func testFailingTLSConnectionClosesConnection() { +// // There was a bug (https://github.com/vapor/postgres-nio/issues/133) where we would hit +// // an assert because we didn't close the connection. This test should succeed without hitting +// // the assert +// +// // postgres://uymgphwj:7_tHbREdRwkqAdu4KoIS7hQnNxr8J1LA@elmer.db.elephantsql.com:5432/uymgphwj +// +// // We should get an error because you can't use an IP address for SNI, but we shouldn't bomb out by +// // hitting the assert +// XCTAssertThrowsError( +// try PostgresConnection.connect( +// to: SocketAddress.makeAddressResolvingHost("elmer.db.elephantsql.com", port: 5432), +// tlsConfiguration: .makeClientConfiguration(), +// serverHostname: "34.228.73.168", +// on: eventLoop +// ).wait() +// ) +// // If we hit this, we're all good +// XCTAssertTrue(true) +// } +// +// @available(*, deprecated, message: "Test deprecated functionality") +// func testInvalidPassword() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.testUnauthenticated(on: eventLoop).wait()) +// let authFuture = conn?.authenticate(username: "invalid", database: "invalid", password: "bad") +// XCTAssertThrowsError(_ = try authFuture?.wait()) { error in +// XCTAssert((error as? PostgresError)?.code == .invalidPassword || (error as? PostgresError)?.code == .invalidAuthorizationSpecification) +// } +// +// // in this case the connection will be closed by the remote +// XCTAssertNoThrow(try conn?.closeFuture.wait()) +// } +// +// func testColumnsInJoin() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// let dateInTable1 = Date(timeIntervalSince1970: 1234) +// let dateInTable2 = Date(timeIntervalSince1970: 5678) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE IF EXISTS \"table1\"").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery(""" +// CREATE TABLE table1 ( +// "id" int8 NOT NULL, +// "table2_id" int8, +// "intValue" int8, +// "stringValue" text, +// "dateValue" timestamptz, +// PRIMARY KEY ("id") +// ); +// """).wait()) +// defer { XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE \"table1\"").wait()) } +// +// XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE IF EXISTS \"table2\"").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery(""" +// CREATE TABLE table2 ( +// "id" int8 NOT NULL, +// "intValue" int8, +// "stringValue" text, +// "dateValue" timestamptz, +// PRIMARY KEY ("id") +// ); +// """).wait()) +// defer { XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE \"table2\"").wait()) } +// +// XCTAssertNoThrow(_ = try conn?.simpleQuery("INSERT INTO table1 VALUES (12, 34, 56, 'stringInTable1', to_timestamp(1234))").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("INSERT INTO table2 VALUES (34, 78, 'stringInTable2', to_timestamp(5678))").wait()) +// +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// SELECT +// "table1"."id" as "t1_id", +// "table1"."intValue" as "t1_intValue", +// "table1"."dateValue" as "t1_dateValue", +// "table1"."stringValue" as "t1_stringValue", +// "table2"."id" as "t2_id", +// "table2"."intValue" as "t2_intValue", +// "table2"."dateValue" as "t2_dateValue", +// "table2"."stringValue" as "t2_stringValue", +// * +// FROM table1 INNER JOIN table2 ON table1.table2_id = table2.id +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "t1_id"].int, 12) +// XCTAssertEqual(row?[data: "table2_id"].int, 34) +// XCTAssertEqual(row?[data: "t1_intValue"].int, 56) +// XCTAssertEqual(row?[data: "t1_stringValue"].string, "stringInTable1") +// XCTAssertEqual(row?[data: "t1_dateValue"].date, dateInTable1) +// XCTAssertEqual(row?[data: "t2_id"].int, 34) +// XCTAssertEqual(row?[data: "t2_intValue"].int, 78) +// XCTAssertEqual(row?[data: "t2_stringValue"].string, "stringInTable2") +// XCTAssertEqual(row?[data: "t2_dateValue"].date, dateInTable2) +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testStringArrays() { +// let query = """ +// SELECT +// $1::uuid as "id", +// $2::bigint as "revision", +// $3::timestamp as "updated_at", +// $4::timestamp as "created_at", +// $5::text as "name", +// $6::text[] as "countries", +// $7::text[] as "languages", +// $8::text[] as "currencies" +// """ +// +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(query, [ +// PostgresData(uuid: UUID(uuidString: "D2710E16-EB07-4FD6-A87E-B1BE41C9BD3D")!), +// PostgresData(int: Int(0)), +// PostgresData(date: Date(timeIntervalSince1970: 0)), +// PostgresData(date: Date(timeIntervalSince1970: 0)), +// PostgresData(string: "Foo"), +// PostgresData(array: ["US"]), +// PostgresData(array: ["en"]), +// PostgresData(array: ["USD", "DKK"]), +// ]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "countries"].array(of: String.self), ["US"]) +// XCTAssertEqual(row?[data: "languages"].array(of: String.self), ["en"]) +// XCTAssertEqual(row?[data: "currencies"].array(of: String.self), ["USD", "DKK"]) +// } +// +// func testBindDate() { +// // https://github.com/vapor/postgres-nio/issues/53 +// let date = Date(timeIntervalSince1970: 1571425782) +// let query = """ +// SELECT $1::json as "date" +// """ +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// XCTAssertThrowsError(_ = try conn?.query(query, [.init(date: date)]).wait()) { error in +// guard let postgresError = try? XCTUnwrap(error as? PostgresError) else { return } +// guard case let .server(serverError) = postgresError else { +// XCTFail("Expected a .serverError but got \(postgresError)") +// return +// } +// XCTAssertEqual(serverError.fields[.routine], "transformTypeCast") +// } +// +// } +// +// func testBindCharString() { +// // https://github.com/vapor/postgres-nio/issues/53 +// let query = """ +// SELECT $1::char as "char" +// """ +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(query, [.init(string: "f")]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "char"].string, "f") +// } +// +// func testBindCharUInt8() { +// // https://github.com/vapor/postgres-nio/issues/53 +// let query = """ +// SELECT $1::char as "char" +// """ +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(query, [.init(uint8: 42)]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "char"].string, "*") +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testDoubleArraySerialization() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// let doubles: [Double] = [3.14, 42] +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// $1::double precision[] as doubles +// """, [ +// .init(array: doubles) +// ]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "doubles"].array(of: Double.self), doubles) +// } +// +// // https://github.com/vapor/postgres-nio/issues/42 +// func testUInt8Serialization() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// $1::"char" as int +// """, [ +// .init(uint8: 5) +// ]).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "int"].uint8, 5) +// } +// +// func testPreparedQuery() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var prepared: PreparedQuery? +// XCTAssertNoThrow(prepared = try conn?.prepare(query: "SELECT 1 as one;").wait()) +// var rows: [PostgresRow]? +// XCTAssertNoThrow(rows = try prepared?.execute().wait()) +// +// XCTAssertEqual(rows?.count, 1) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "one"].int, 1) +// } +// +// func testPrepareQueryClosure() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var queries: [[PostgresRow]]? +// XCTAssertNoThrow(queries = try conn?.prepare(query: "SELECT $1::text as foo;", handler: { [eventLoop] query in +// let a = query.execute(["a"]) +// let b = query.execute(["b"]) +// let c = query.execute(["c"]) +// return EventLoopFuture.whenAllSucceed([a, b, c], on: eventLoop) +// }).wait()) +// XCTAssertEqual(queries?.count, 3) +// var resultIterator = queries?.makeIterator() +// XCTAssertEqual(try resultIterator?.next()?.first?.decode(String.self, context: .default), "a") +// XCTAssertEqual(try resultIterator?.next()?.first?.decode(String.self, context: .default), "b") +// XCTAssertEqual(try resultIterator?.next()?.first?.decode(String.self, context: .default), "c") +// } +// +// // https://github.com/vapor/postgres-nio/issues/122 +// func testPreparedQueryNoResults() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE IF EXISTS \"table_no_results\"").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery(""" +// CREATE TABLE table_no_results ( +// "id" int8 NOT NULL, +// "stringValue" text, +// PRIMARY KEY ("id") +// ); +// """).wait()) +// defer { XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE \"table_no_results\"").wait()) } +// +// XCTAssertNoThrow(_ = try conn?.prepare(query: "DELETE FROM \"table_no_results\" WHERE id = $1").wait()) +// } +// +// +// // https://github.com/vapor/postgres-nio/issues/71 +// func testChar1Serialization() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// '5'::char(1) as one, +// '5'::char(2) as two +// """).wait()) +// +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "one"].uint8, 53) +// XCTAssertEqual(row?[data: "one"].int16, 53) +// XCTAssertEqual(row?[data: "one"].string, "5") +// XCTAssertEqual(row?[data: "two"].uint8, nil) +// XCTAssertEqual(row?[data: "two"].int16, nil) +// XCTAssertEqual(row?[data: "two"].string, "5 ") +// } +// +// func testUserDefinedType() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// XCTAssertNoThrow(_ = try conn?.query("DROP TYPE IF EXISTS foo").wait()) +// XCTAssertNoThrow(_ = try conn?.query("CREATE TYPE foo AS ENUM ('bar', 'qux')").wait()) +// defer { +// XCTAssertNoThrow(_ = try conn?.query("DROP TYPE foo").wait()) +// } +// var res: PostgresQueryResult? +// XCTAssertNoThrow(res = try conn?.query("SELECT 'qux'::foo as foo").wait()) +// let row = res?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "foo"].string, "qux") +// } +// +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testNullBind() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// var res: PostgresQueryResult? +// XCTAssertNoThrow(res = try conn?.query("SELECT $1::text as foo", [String?.none.postgresData!]).wait()) +// let row = res?.first?.makeRandomAccess() +// XCTAssertNil(row?[data: "foo"].string) +// } +// +// func testUpdateMetadata() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// XCTAssertNoThrow(_ = try conn?.simpleQuery("DROP TABLE IF EXISTS test_table").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("CREATE TABLE test_table(pk int PRIMARY KEY)").wait()) +// XCTAssertNoThrow(_ = try conn?.simpleQuery("INSERT INTO test_table VALUES(1)").wait()) +// XCTAssertNoThrow(try conn?.query("DELETE FROM test_table", onMetadata: { metadata in +// XCTAssertEqual(metadata.command, "DELETE") +// XCTAssertEqual(metadata.oid, nil) +// XCTAssertEqual(metadata.rows, 1) +// }, onRow: { _ in }).wait()) +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query("DELETE FROM test_table").wait()) +// XCTAssertEqual(rows?.metadata.command, "DELETE") +// XCTAssertEqual(rows?.metadata.oid, nil) +// XCTAssertEqual(rows?.metadata.rows, 0) +// } +// +// func testTooManyBinds() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// let binds = [PostgresData].init(repeating: .null, count: Int(UInt16.max) + 1) +// XCTAssertThrowsError(try conn?.query("SELECT version()", binds).wait()) { error in +// guard case .tooManyParameters = (error as? PSQLError)?.code.base else { +// return XCTFail("Unexpected error: \(error)") +// } +// } +// } +// +// func testRemoteClose() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// XCTAssertNoThrow( try conn?.channel.close().wait() ) +// } +// +// // https://github.com/vapor/postgres-nio/issues/113 +// @available(*, deprecated, message: "Testing deprecated functionality") +// func testVaryingCharArray() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// var res: PostgresQueryResult? +// XCTAssertNoThrow(res = try conn?.query(#"SELECT '{"foo", "bar", "baz"}'::VARCHAR[] as foo"#).wait()) +// let row = res?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "foo"].array(of: String.self), ["foo", "bar", "baz"]) +// } +// +// // https://github.com/vapor/postgres-nio/issues/115 +// func testSetTimeZone() { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// +// XCTAssertNoThrow(_ = try conn?.simpleQuery("SET TIME ZONE INTERVAL '+5:45' HOUR TO MINUTE").wait()) +// XCTAssertNoThrow(_ = try conn?.query("SET TIME ZONE INTERVAL '+5:45' HOUR TO MINUTE").wait()) +// } +// +// func testIntegerConversions() throws { +// var conn: PostgresConnection? +// XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) +// defer { XCTAssertNoThrow( try conn?.close().wait() ) } +// var rows: PostgresQueryResult? +// XCTAssertNoThrow(rows = try conn?.query(""" +// select +// 'a'::char as test8, +// +// '-32768'::smallint as min16, +// '32767'::smallint as max16, +// +// '-2147483648'::integer as min32, +// '2147483647'::integer as max32, +// +// '-9223372036854775808'::bigint as min64, +// '9223372036854775807'::bigint as max64 +// """).wait()) +// let row = rows?.first?.makeRandomAccess() +// XCTAssertEqual(row?[data: "test8"].uint8, 97) +// XCTAssertEqual(row?[data: "test8"].int16, 97) +// XCTAssertEqual(row?[data: "test8"].int32, 97) +// XCTAssertEqual(row?[data: "test8"].int64, 97) +// +// XCTAssertEqual(row?[data: "min16"].uint8, nil) +// XCTAssertEqual(row?[data: "max16"].uint8, nil) +// XCTAssertEqual(row?[data: "min16"].int16, .min) +// XCTAssertEqual(row?[data: "max16"].int16, .max) +// XCTAssertEqual(row?[data: "min16"].int32, -32768) +// XCTAssertEqual(row?[data: "max16"].int32, 32767) +// XCTAssertEqual(row?[data: "min16"].int64, -32768) +// XCTAssertEqual(row?[data: "max16"].int64, 32767) +// +// XCTAssertEqual(row?[data: "min32"].uint8, nil) +// XCTAssertEqual(row?[data: "max32"].uint8, nil) +// XCTAssertEqual(row?[data: "min32"].int16, nil) +// XCTAssertEqual(row?[data: "max32"].int16, nil) +// XCTAssertEqual(row?[data: "min32"].int32, .min) +// XCTAssertEqual(row?[data: "max32"].int32, .max) +// XCTAssertEqual(row?[data: "min32"].int64, -2147483648) +// XCTAssertEqual(row?[data: "max32"].int64, 2147483647) +// +// XCTAssertEqual(row?[data: "min64"].uint8, nil) +// XCTAssertEqual(row?[data: "max64"].uint8, nil) +// XCTAssertEqual(row?[data: "min64"].int16, nil) +// XCTAssertEqual(row?[data: "max64"].int16, nil) +// XCTAssertEqual(row?[data: "min64"].int32, nil) +// XCTAssertEqual(row?[data: "max64"].int32, nil) +// XCTAssertEqual(row?[data: "min64"].int64, .min) +// XCTAssertEqual(row?[data: "max64"].int64, .max) +// } } let isLoggingConfigured: Bool = { diff --git a/Tests/IntegrationTests/Utilities.swift b/Tests/IntegrationTests/Utilities.swift index 91dbb62e..e14ce7ff 100644 --- a/Tests/IntegrationTests/Utilities.swift +++ b/Tests/IntegrationTests/Utilities.swift @@ -12,17 +12,6 @@ extension PostgresConnection { static func address() throws -> SocketAddress { try .makeAddressResolvingHost(env("POSTGRES_HOSTNAME") ?? "localhost", port: env("POSTGRES_PORT").flatMap(Int.init(_:)) ?? 5432) } - - @available(*, deprecated, message: "Test deprecated functionality") - static func testUnauthenticated(on eventLoop: EventLoop, logLevel: Logger.Level = .info) -> EventLoopFuture<PostgresConnection> { - var logger = Logger(label: "postgres.connection.test") - logger.logLevel = logLevel - do { - return connect(to: try address(), logger: logger, on: eventLoop) - } catch { - return eventLoop.makeFailedFuture(error) - } - } static func test(on eventLoop: EventLoop, options: Configuration.Options? = nil) -> EventLoopFuture<PostgresConnection> { let logger = Logger(label: "postgres.connection.test") diff --git a/Tests/PostgresNIOTests/Data/PostgresData+JSONTests.swift b/Tests/PostgresNIOTests/Data/PostgresData+JSONTests.swift deleted file mode 100644 index 47dd89a1..00000000 --- a/Tests/PostgresNIOTests/Data/PostgresData+JSONTests.swift +++ /dev/null @@ -1,21 +0,0 @@ -import PostgresNIO -import XCTest - -class PostgresData_JSONTests: XCTestCase { - @available(*, deprecated, message: "Testing deprecated functionality") - func testJSONBConvertible() { - struct Object: PostgresJSONBCodable { - let foo: Int - let bar: Int - } - - XCTAssertEqual(Object.postgresDataType, .jsonb) - - let postgresData = Object(foo: 1, bar: 2).postgresData - XCTAssertEqual(postgresData?.type, .jsonb) - - let object = Object(postgresData: postgresData!) - XCTAssertEqual(object?.foo, 1) - XCTAssertEqual(object?.bar, 2) - } -} diff --git a/Tests/PostgresNIOTests/Message/PostgresMessageDecoderTests.swift b/Tests/PostgresNIOTests/Message/PostgresMessageDecoderTests.swift deleted file mode 100644 index bbd022db..00000000 --- a/Tests/PostgresNIOTests/Message/PostgresMessageDecoderTests.swift +++ /dev/null @@ -1,39 +0,0 @@ -import PostgresNIO -import XCTest -import NIOTestUtils -import NIOCore - -class PostgresMessageDecoderTests: XCTestCase { - @available(*, deprecated, message: "Tests deprecated API") - func testMessageDecoder() { - let sample: [UInt8] = [ - 0x52, // R - authentication - 0x00, 0x00, 0x00, 0x0C, // length = 12 - 0x00, 0x00, 0x00, 0x05, // md5 - 0x01, 0x02, 0x03, 0x04, // salt - 0x4B, // B - backend key data - 0x00, 0x00, 0x00, 0x0C, // length = 12 - 0x05, 0x05, 0x05, 0x05, // process id - 0x01, 0x01, 0x01, 0x01, // secret key - ] - var input = ByteBufferAllocator().buffer(capacity: 0) - input.writeBytes(sample) - - let output: [PostgresMessage] = [ - PostgresMessage(identifier: .authentication, bytes: [ - 0x00, 0x00, 0x00, 0x05, - 0x01, 0x02, 0x03, 0x04, - ]), - PostgresMessage(identifier: .backendKeyData, bytes: [ - 0x05, 0x05, 0x05, 0x05, - 0x01, 0x01, 0x01, 0x01, - ]) - ] - XCTAssertNoThrow(try XCTUnwrap(ByteToMessageDecoderVerifier.verifyDecoder( - inputOutputPairs: [(input, output)], - decoderFactory: { - PostgresMessageDecoder() - } - ))) - } -} diff --git a/Tests/PostgresNIOTests/New/Connection State Machine/AuthenticationStateMachineTests.swift b/Tests/PostgresNIOTests/New/Connection State Machine/AuthenticationStateMachineTests.swift index df881f90..92a03206 100644 --- a/Tests/PostgresNIOTests/New/Connection State Machine/AuthenticationStateMachineTests.swift +++ b/Tests/PostgresNIOTests/New/Connection State Machine/AuthenticationStateMachineTests.swift @@ -93,7 +93,7 @@ class AuthenticationStateMachineTests: XCTestCase { // MARK: Test unsupported messages func testUnsupportedAuthMechanism() { - let unsupported: [(PostgresBackendMessage.Authentication, PSQLError.UnsupportedAuthScheme)] = [ + let unsupported: [(PostgresBackendMessage.Authentication, PostgresError.UnsupportedAuthScheme)] = [ (.kerberosV5, .kerberosV5), (.scmCredential, .scmCredential), (.gss, .gss), diff --git a/Tests/PostgresNIOTests/New/Connection State Machine/ConnectionStateMachineTests.swift b/Tests/PostgresNIOTests/New/Connection State Machine/ConnectionStateMachineTests.swift index f3d72a5e..0d3bff4f 100644 --- a/Tests/PostgresNIOTests/New/Connection State Machine/ConnectionStateMachineTests.swift +++ b/Tests/PostgresNIOTests/New/Connection State Machine/ConnectionStateMachineTests.swift @@ -30,7 +30,7 @@ class ConnectionStateMachineTests: XCTestCase { func testSSLStartupFailureTooManyBytesRemaining() { var state = ConnectionStateMachine(requireBackendKeyData: true) XCTAssertEqual(state.connected(tls: .require), .sendSSLRequest) - let failError = PSQLError.receivedUnencryptedDataAfterSSLRequest + let failError = PostgresError.receivedUnencryptedDataAfterSSLRequest XCTAssertEqual(state.sslSupportedReceived(unprocessedBytes: 1), .closeConnectionAndCleanup(.init(action: .close, tasks: [], error: failError, closePromise: nil))) } @@ -40,7 +40,7 @@ class ConnectionStateMachineTests: XCTestCase { var state = ConnectionStateMachine(requireBackendKeyData: true) XCTAssertEqual(state.connected(tls: .require), .sendSSLRequest) XCTAssertEqual(state.sslSupportedReceived(unprocessedBytes: 0), .establishSSLConnection) - let failError = PSQLError.failedToAddSSLHandler(underlying: SSLHandlerAddError()) + let failError = PostgresError.failedToAddSSLHandler(underlying: SSLHandlerAddError()) XCTAssertEqual(state.errorHappened(failError), .closeConnectionAndCleanup(.init(action: .close, tasks: [], error: failError, closePromise: nil))) } @@ -49,7 +49,7 @@ class ConnectionStateMachineTests: XCTestCase { XCTAssertEqual(state.connected(tls: .require), .sendSSLRequest) XCTAssertEqual(state.sslUnsupportedReceived(), - .closeConnectionAndCleanup(.init(action: .close, tasks: [], error: PSQLError.sslUnsupported, closePromise: nil))) + .closeConnectionAndCleanup(.init(action: .close, tasks: [], error: PostgresError.sslUnsupported, closePromise: nil))) } func testTLSPreferredStartupSSLUnsupported() { @@ -114,7 +114,7 @@ class ConnectionStateMachineTests: XCTestCase { XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "standard_conforming_strings", value: "on")), .wait) XCTAssertEqual(state.readyForQueryReceived(.idle), - .closeConnectionAndCleanup(.init(action: .close, tasks: [], error: PSQLError.unexpectedBackendMessage(.readyForQuery(.idle)), closePromise: nil))) + .closeConnectionAndCleanup(.init(action: .close, tasks: [], error: PostgresError.unexpectedBackendMessage(.readyForQuery(.idle)), closePromise: nil))) } func testReadyForQueryReceivedWithoutUnneededBackendKeyAfterAuthenticated() { @@ -183,6 +183,6 @@ class ConnectionStateMachineTests: XCTestCase { XCTAssertNil(queryPromise.futureResult._value) // make sure we don't crash - queryPromise.fail(PSQLError.server(.init(fields: fields))) + queryPromise.fail(PostgresError.server(.init(fields: fields))) } } diff --git a/Tests/PostgresNIOTests/New/Connection State Machine/ExtendedQueryStateMachineTests.swift b/Tests/PostgresNIOTests/New/Connection State Machine/ExtendedQueryStateMachineTests.swift index ae484acc..a853e0f9 100644 --- a/Tests/PostgresNIOTests/New/Connection State Machine/ExtendedQueryStateMachineTests.swift +++ b/Tests/PostgresNIOTests/New/Connection State Machine/ExtendedQueryStateMachineTests.swift @@ -11,7 +11,7 @@ class ExtendedQueryStateMachineTests: XCTestCase { let logger = Logger.psqlTest let promise = EmbeddedEventLoop().makePromise(of: PSQLRowStream.self) - promise.fail(PSQLError.uncleanShutdown) // we don't care about the error at all. + promise.fail(PostgresError.uncleanShutdown) // we don't care about the error at all. let query: PostgresQuery = "DELETE FROM table WHERE id=\(1)" let queryContext = ExtendedQueryContext(query: query, logger: logger, promise: promise) @@ -29,7 +29,7 @@ class ExtendedQueryStateMachineTests: XCTestCase { let logger = Logger.psqlTest let promise = EmbeddedEventLoop().makePromise(of: PSQLRowStream.self) - promise.fail(PSQLError.uncleanShutdown) // we don't care about the error at all. + promise.fail(PostgresError.uncleanShutdown) // we don't care about the error at all. let query: PostgresQuery = "SELECT version()" let queryContext = ExtendedQueryContext(query: query, logger: logger, promise: promise) @@ -83,7 +83,7 @@ class ExtendedQueryStateMachineTests: XCTestCase { let logger = Logger.psqlTest let promise = EmbeddedEventLoop().makePromise(of: PSQLRowStream.self) - promise.fail(PSQLError.uncleanShutdown) // we don't care about the error at all. + promise.fail(PostgresError.uncleanShutdown) // we don't care about the error at all. let query: PostgresQuery = "-- some comments" let queryContext = ExtendedQueryContext(query: query, logger: logger, promise: promise) @@ -101,7 +101,7 @@ class ExtendedQueryStateMachineTests: XCTestCase { let logger = Logger.psqlTest let promise = EmbeddedEventLoop().makePromise(of: PSQLRowStream.self) - promise.fail(PSQLError.uncleanShutdown) // we don't care about the error at all. + promise.fail(PostgresError.uncleanShutdown) // we don't care about the error at all. let query: PostgresQuery = "DELETE FROM table WHERE id=\(1)" let queryContext = ExtendedQueryContext(query: query, logger: logger, promise: promise) @@ -109,7 +109,7 @@ class ExtendedQueryStateMachineTests: XCTestCase { XCTAssertEqual(state.parseCompleteReceived(), .wait) XCTAssertEqual(state.parameterDescriptionReceived(.init(dataTypes: [.int8])), .wait) - let psqlError = PSQLError.unexpectedBackendMessage(.authentication(.ok)) + let psqlError = PostgresError.unexpectedBackendMessage(.authentication(.ok)) XCTAssertEqual(state.authenticationMessageReceived(.ok), .failQuery(promise, with: psqlError, cleanupContext: .init(action: .close, tasks: [], error: psqlError, closePromise: nil))) } @@ -119,7 +119,7 @@ class ExtendedQueryStateMachineTests: XCTestCase { let logger = Logger.psqlTest let promise = EmbeddedEventLoop().makePromise(of: PSQLRowStream.self) - promise.fail(PSQLError.uncleanShutdown) // we don't care about the error at all. + promise.fail(PostgresError.uncleanShutdown) // we don't care about the error at all. let query: PostgresQuery = "SELECT version()" let queryContext = ExtendedQueryContext(query: query, logger: logger, promise: promise) @@ -163,7 +163,7 @@ class ExtendedQueryStateMachineTests: XCTestCase { let logger = Logger.psqlTest let promise = EmbeddedEventLoop().makePromise(of: PSQLRowStream.self) - promise.fail(PSQLError.uncleanShutdown) // we don't care about the error at all. + promise.fail(PostgresError.uncleanShutdown) // we don't care about the error at all. let query: PostgresQuery = "SELECT version()" let queryContext = ExtendedQueryContext(query: query, logger: logger, promise: promise) @@ -205,7 +205,7 @@ class ExtendedQueryStateMachineTests: XCTestCase { let logger = Logger.psqlTest let promise = EmbeddedEventLoop().makePromise(of: PSQLRowStream.self) - promise.fail(PSQLError.uncleanShutdown) // we don't care about the error at all. + promise.fail(PostgresError.uncleanShutdown) // we don't care about the error at all. let query: PostgresQuery = "SELECT version()" let queryContext = ExtendedQueryContext(query: query, logger: logger, promise: promise) @@ -259,7 +259,7 @@ class ExtendedQueryStateMachineTests: XCTestCase { let logger = Logger.psqlTest let promise = EmbeddedEventLoop().makePromise(of: PSQLRowStream.self) - promise.fail(PSQLError.uncleanShutdown) // we don't care about the error at all. + promise.fail(PostgresError.uncleanShutdown) // we don't care about the error at all. let query: PostgresQuery = "SELECT version()" let queryContext = ExtendedQueryContext(query: query, logger: logger, promise: promise) @@ -280,7 +280,7 @@ class ExtendedQueryStateMachineTests: XCTestCase { let logger = Logger.psqlTest let promise = EmbeddedEventLoop().makePromise(of: PSQLRowStream.self) - promise.fail(PSQLError.uncleanShutdown) // we don't care about the error at all. + promise.fail(PostgresError.uncleanShutdown) // we don't care about the error at all. let query: PostgresQuery = "SELECT version()" let queryContext = ExtendedQueryContext(query: query, logger: logger, promise: promise) diff --git a/Tests/PostgresNIOTests/New/Connection State Machine/PrepareStatementStateMachineTests.swift b/Tests/PostgresNIOTests/New/Connection State Machine/PrepareStatementStateMachineTests.swift index 547f5cdf..62eae0b2 100644 --- a/Tests/PostgresNIOTests/New/Connection State Machine/PrepareStatementStateMachineTests.swift +++ b/Tests/PostgresNIOTests/New/Connection State Machine/PrepareStatementStateMachineTests.swift @@ -7,7 +7,7 @@ class PrepareStatementStateMachineTests: XCTestCase { var state = ConnectionStateMachine.readyForQuery() let promise = EmbeddedEventLoop().makePromise(of: RowDescription?.self) - promise.fail(PSQLError.uncleanShutdown) // we don't care about the error at all. + promise.fail(PostgresError.uncleanShutdown) // we don't care about the error at all. let name = "haha" let query = #"SELECT id FROM users WHERE id = $1 "# @@ -33,7 +33,7 @@ class PrepareStatementStateMachineTests: XCTestCase { var state = ConnectionStateMachine.readyForQuery() let promise = EmbeddedEventLoop().makePromise(of: RowDescription?.self) - promise.fail(PSQLError.uncleanShutdown) // we don't care about the error at all. + promise.fail(PostgresError.uncleanShutdown) // we don't care about the error at all. let name = "haha" let query = #"DELETE FROM users WHERE id = $1 "# @@ -55,7 +55,7 @@ class PrepareStatementStateMachineTests: XCTestCase { var state = ConnectionStateMachine.readyForQuery() let promise = EmbeddedEventLoop().makePromise(of: RowDescription?.self) - promise.fail(PSQLError.uncleanShutdown) // we don't care about the error at all. + promise.fail(PostgresError.uncleanShutdown) // we don't care about the error at all. let name = "haha" let query = #"DELETE FROM users WHERE id = $1 "# diff --git a/Tests/PostgresNIOTests/New/Connection State Machine/PreparedStatementStateMachineTests.swift b/Tests/PostgresNIOTests/New/Connection State Machine/PreparedStatementStateMachineTests.swift index e35e93f7..e3c03054 100644 --- a/Tests/PostgresNIOTests/New/Connection State Machine/PreparedStatementStateMachineTests.swift +++ b/Tests/PostgresNIOTests/New/Connection State Machine/PreparedStatementStateMachineTests.swift @@ -69,7 +69,7 @@ class PreparedStatementStateMachineTests: XCTestCase { } // Simulate an error occurring during preparation - let error = PSQLError(code: .server) + let error = PostgresError(code: .server) let preparationCompleteAction = stateMachine.errorHappened( name: "test", error: error diff --git a/Tests/PostgresNIOTests/New/Extensions/ConnectionAction+TestUtils.swift b/Tests/PostgresNIOTests/New/Extensions/ConnectionAction+TestUtils.swift index 9a1224d8..fcf31882 100644 --- a/Tests/PostgresNIOTests/New/Extensions/ConnectionAction+TestUtils.swift +++ b/Tests/PostgresNIOTests/New/Extensions/ConnectionAction+TestUtils.swift @@ -99,8 +99,8 @@ extension ConnectionStateMachine { } // fully-qualifying all types in the extension has the same effect as adding a `@retroactive` before the protocol -extension PostgresNIO.PSQLError: Swift.Equatable { - public static func == (lhs: PSQLError, rhs: PSQLError) -> Bool { +extension PostgresNIO.PostgresError: Swift.Equatable { + public static func == (lhs: PostgresError, rhs: PostgresError) -> Bool { return true } } diff --git a/Tests/PostgresNIOTests/New/PSQLRowStreamTests.swift b/Tests/PostgresNIOTests/New/PSQLRowStreamTests.swift index 65ca26c3..c2dbab4e 100644 --- a/Tests/PostgresNIOTests/New/PSQLRowStreamTests.swift +++ b/Tests/PostgresNIOTests/New/PSQLRowStreamTests.swift @@ -23,13 +23,13 @@ final class PSQLRowStreamTests: XCTestCase { func testFailedStream() { let stream = PSQLRowStream( - source: .noRows(.failure(PSQLError.serverClosedConnection(underlying: nil))), + source: .noRows(.failure(PostgresError.serverClosedConnection(underlying: nil))), eventLoop: self.eventLoop, logger: self.logger ) XCTAssertThrowsError(try stream.all().wait()) { - XCTAssertEqual($0 as? PSQLError, .serverClosedConnection(underlying: nil)) + XCTAssertEqual($0 as? PostgresError, .serverClosedConnection(underlying: nil)) } } diff --git a/Tests/PostgresNIOTests/New/PostgresChannelHandlerTests.swift b/Tests/PostgresNIOTests/New/PostgresChannelHandlerTests.swift index a2c90969..4bd7816c 100644 --- a/Tests/PostgresNIOTests/New/PostgresChannelHandlerTests.swift +++ b/Tests/PostgresNIOTests/New/PostgresChannelHandlerTests.swift @@ -116,7 +116,7 @@ class PostgresChannelHandlerTests: XCTestCase { let handler = PostgresChannelHandler(configuration: config, eventLoop: self.eventLoop) { channel, _ in XCTFail("This callback should never be exectuded") - throw PSQLError.sslUnsupported + throw PostgresError.sslUnsupported } let embedded = EmbeddedChannel(handlers: [ ReverseByteToMessageHandler(PSQLFrontendMessageDecoder()), @@ -145,18 +145,17 @@ class PostgresChannelHandlerTests: XCTestCase { func testRunAuthenticateMD5Password() { let config = self.testConnectionConfiguration() let authContext = AuthContext( - username: config.username ?? "something wrong", + username: config.username, password: config.password, database: config.database ) - let state = ConnectionStateMachine(.waitingToStartAuthentication) - let handler = PostgresChannelHandler(configuration: config, eventLoop: self.eventLoop, state: state, configureSSLCallback: nil) + let handler = PostgresChannelHandler(configuration: config, eventLoop: self.eventLoop, configureSSLCallback: nil) let embedded = EmbeddedChannel(handlers: [ ReverseByteToMessageHandler(PSQLFrontendMessageDecoder()), handler ], loop: self.eventLoop) - embedded.triggerUserOutboundEvent(PSQLOutgoingEvent.authenticate(authContext), promise: nil) + XCTAssertNoThrow(try embedded.connect(to: .init(ipAddress: "127.0.0.1", port: 5432))) XCTAssertEqual(try embedded.readOutbound(as: PostgresFrontendMessage.self), .startup(.versionThree(parameters: authContext.toStartupParameters()))) let salt: UInt32 = 0x00_01_02_03 @@ -172,19 +171,18 @@ class PostgresChannelHandlerTests: XCTestCase { let password = "postgres" let config = self.testConnectionConfiguration(password: password) let authContext = AuthContext( - username: config.username ?? "something wrong", + username: config.username, password: config.password, database: config.database ) - let state = ConnectionStateMachine(.waitingToStartAuthentication) - let handler = PostgresChannelHandler(configuration: config, eventLoop: self.eventLoop, state: state, configureSSLCallback: nil) + let handler = PostgresChannelHandler(configuration: config, eventLoop: self.eventLoop, configureSSLCallback: nil) let embedded = EmbeddedChannel(handlers: [ ReverseByteToMessageHandler(PSQLFrontendMessageDecoder()), ReverseMessageToByteHandler(PSQLBackendMessageEncoder()), handler ], loop: self.eventLoop) - embedded.triggerUserOutboundEvent(PSQLOutgoingEvent.authenticate(authContext), promise: nil) + XCTAssertNoThrow(try embedded.connect(to: .init(ipAddress: "127.0.0.1", port: 5432))) XCTAssertEqual(try embedded.readOutbound(as: PostgresFrontendMessage.self), .startup(.versionThree(parameters: authContext.toStartupParameters()))) XCTAssertNoThrow(try embedded.writeInbound(PostgresBackendMessage.authentication(.plaintext))) @@ -258,11 +256,11 @@ class PostgresChannelHandlerTests: XCTestCase { class TestEventHandler: ChannelInboundHandler { typealias InboundIn = Never - var errors = [PSQLError]() + var errors = [PostgresError]() var events = [PSQLEvent]() func errorCaught(context: ChannelHandlerContext, error: Error) { - guard let psqlError = error as? PSQLError else { + guard let psqlError = error as? PostgresError else { return XCTFail("Unexpected error type received: \(error)") } self.errors.append(psqlError) diff --git a/Tests/PostgresNIOTests/New/PostgresConnectionTests.swift b/Tests/PostgresNIOTests/New/PostgresConnectionTests.swift index 0bc61efd..6e71b073 100644 --- a/Tests/PostgresNIOTests/New/PostgresConnectionTests.swift +++ b/Tests/PostgresNIOTests/New/PostgresConnectionTests.swift @@ -34,7 +34,7 @@ class PostgresConnectionTests: XCTestCase { logger.logLevel = .trace XCTAssertThrowsError(try PostgresConnection.connect(on: eventLoopGroup.next(), configuration: config, id: 1, logger: logger).wait()) { - XCTAssertTrue($0 is PSQLError) + XCTAssertTrue($0 is PostgresError) } } @@ -308,7 +308,7 @@ class PostgresConnectionTests: XCTestCase { case .success: XCTFail("Expected queries to fail") case .failure(let failure): - guard let error = failure as? PSQLError else { + guard let error = failure as? PostgresError else { return XCTFail("Unexpected error type: \(failure)") } XCTAssertEqual(error.code, .clientClosedConnection) @@ -333,7 +333,7 @@ class PostgresConnectionTests: XCTestCase { _ = try await response XCTFail("Expected to throw") } catch { - XCTAssertEqual((error as? PSQLError)?.code, .serverClosedConnection) + XCTAssertEqual((error as? PostgresError)?.code, .serverClosedConnection) } // retry on same connection @@ -342,7 +342,7 @@ class PostgresConnectionTests: XCTestCase { _ = try await connection.query("SELECT 1;", logger: self.logger) XCTFail("Expected to throw") } catch { - XCTAssertEqual((error as? PSQLError)?.code, .serverClosedConnection) + XCTAssertEqual((error as? PostgresError)?.code, .serverClosedConnection) } } @@ -354,7 +354,7 @@ class PostgresConnectionTests: XCTestCase { func makeBindings() -> PostgresBindings { var bindings = PostgresBindings() - bindings.append(.init(string: self.state)) + bindings.append(self.state) return bindings } @@ -602,7 +602,7 @@ class PostgresConnectionTests: XCTestCase { _ = try await connection.execute(preparedStatement, logger: .psqlTest) XCTFail("Was supposed to fail") } catch { - XCTAssert(error is PSQLError) + XCTAssert(error is PostgresError) } } @@ -632,7 +632,7 @@ class PostgresConnectionTests: XCTestCase { _ = try await connection.execute(preparedStatement, logger: .psqlTest) XCTFail("Was supposed to fail") } catch { - XCTAssert(error is PSQLError) + XCTAssert(error is PostgresError) } } } diff --git a/Tests/PostgresNIOTests/New/PostgresErrorTests.swift b/Tests/PostgresNIOTests/New/PostgresErrorTests.swift index 33df5439..c655d053 100644 --- a/Tests/PostgresNIOTests/New/PostgresErrorTests.swift +++ b/Tests/PostgresNIOTests/New/PostgresErrorTests.swift @@ -37,7 +37,7 @@ final class PSQLErrorTests: XCTestCase { } func testPSQLErrorDescription() { - var error1 = PSQLError.server(.init(fields: [.localizedSeverity: "ERROR", .severity: "ERROR", .sqlState: "00000", .message: "Test message", .detail: "More test message", .hint: "It's a test, that's your hint", .position: "1", .schemaName: "testsch", .tableName: "testtab", .columnName: "testcol", .dataTypeName: "testtyp", .constraintName: "testcon", .file: #fileID, .line: "0", .routine: #function])) + var error1 = PostgresError.server(.init(fields: [.localizedSeverity: "ERROR", .severity: "ERROR", .sqlState: "00000", .message: "Test message", .detail: "More test message", .hint: "It's a test, that's your hint", .position: "1", .schemaName: "testsch", .tableName: "testtab", .columnName: "testcol", .dataTypeName: "testtyp", .constraintName: "testcon", .file: #fileID, .line: "0", .routine: #function])) var testBinds = PostgresBindings(capacity: 1) testBinds.append(1, context: .default) error1.query = .init(unsafeSQL: "TEST QUERY", binds: testBinds) diff --git a/Tests/PostgresNIOTests/New/PostgresRowSequenceTests.swift b/Tests/PostgresNIOTests/New/PostgresRowSequenceTests.swift index 816daf04..ef55d978 100644 --- a/Tests/PostgresNIOTests/New/PostgresRowSequenceTests.swift +++ b/Tests/PostgresNIOTests/New/PostgresRowSequenceTests.swift @@ -183,7 +183,7 @@ final class PostgresRowSequenceTests: XCTestCase { logger: self.logger ) - stream.receive(completion: .failure(PSQLError.serverClosedConnection(underlying: nil))) + stream.receive(completion: .failure(PostgresError.serverClosedConnection(underlying: nil))) let rowSequence = stream.asyncSequence() @@ -194,7 +194,7 @@ final class PostgresRowSequenceTests: XCTestCase { } XCTFail("Expected that an error was thrown before.") } catch { - XCTAssertEqual(error as? PSQLError, .serverClosedConnection(underlying: nil)) + XCTAssertEqual(error as? PostgresError, .serverClosedConnection(underlying: nil)) } } @@ -255,14 +255,14 @@ final class PostgresRowSequenceTests: XCTestCase { XCTAssertEqual(try row1?.decode(Int.self), 0) DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) { - stream.receive(completion: .failure(PSQLError.serverClosedConnection(underlying: nil))) + stream.receive(completion: .failure(PostgresError.serverClosedConnection(underlying: nil))) } do { _ = try await rowIterator.next() XCTFail("Expected that an error was thrown before.") } catch { - XCTAssertEqual(error as? PSQLError, .serverClosedConnection(underlying: nil)) + XCTAssertEqual(error as? PostgresError, .serverClosedConnection(underlying: nil)) } } diff --git a/Tests/PostgresNIOTests/Utilities/PostgresJSONCodingTests.swift b/Tests/PostgresNIOTests/Utilities/PostgresJSONCodingTests.swift deleted file mode 100644 index c6f876f2..00000000 --- a/Tests/PostgresNIOTests/Utilities/PostgresJSONCodingTests.swift +++ /dev/null @@ -1,66 +0,0 @@ -import Atomics -import NIOCore -import XCTest -import PostgresNIO - -class PostgresJSONCodingTests: XCTestCase { - // https://github.com/vapor/postgres-nio/issues/126 - func testCustomJSONEncoder() { - let previousDefaultJSONEncoder = PostgresNIO._defaultJSONEncoder - defer { - PostgresNIO._defaultJSONEncoder = previousDefaultJSONEncoder - } - final class CustomJSONEncoder: PostgresJSONEncoder { - let counter = ManagedAtomic(0) - func encode<T>(_ value: T) throws -> Data where T : Encodable { - self.counter.wrappingIncrement(ordering: .relaxed) - return try JSONEncoder().encode(value) - } - } - struct Object: Codable { - var foo: Int - var bar: Int - } - let customJSONEncoder = CustomJSONEncoder() - XCTAssertEqual(customJSONEncoder.counter.load(ordering: .relaxed), 0) - PostgresNIO._defaultJSONEncoder = customJSONEncoder - XCTAssertNoThrow(try PostgresData(json: Object(foo: 1, bar: 2))) - XCTAssertEqual(customJSONEncoder.counter.load(ordering: .relaxed), 1) - - let customJSONBEncoder = CustomJSONEncoder() - XCTAssertEqual(customJSONBEncoder.counter.load(ordering: .relaxed), 0) - PostgresNIO._defaultJSONEncoder = customJSONBEncoder - XCTAssertNoThrow(try PostgresData(json: Object(foo: 1, bar: 2))) - XCTAssertEqual(customJSONBEncoder.counter.load(ordering: .relaxed), 1) - } - - // https://github.com/vapor/postgres-nio/issues/126 - func testCustomJSONDecoder() { - let previousDefaultJSONDecoder = PostgresNIO._defaultJSONDecoder - defer { - PostgresNIO._defaultJSONDecoder = previousDefaultJSONDecoder - } - final class CustomJSONDecoder: PostgresJSONDecoder { - let counter = ManagedAtomic(0) - func decode<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable { - self.counter.wrappingIncrement(ordering: .relaxed) - return try JSONDecoder().decode(type, from: data) - } - } - struct Object: Codable { - var foo: Int - var bar: Int - } - let customJSONDecoder = CustomJSONDecoder() - XCTAssertEqual(customJSONDecoder.counter.load(ordering: .relaxed), 0) - PostgresNIO._defaultJSONDecoder = customJSONDecoder - XCTAssertNoThrow(try PostgresData(json: Object(foo: 1, bar: 2)).json(as: Object.self)) - XCTAssertEqual(customJSONDecoder.counter.load(ordering: .relaxed), 1) - - let customJSONBDecoder = CustomJSONDecoder() - XCTAssertEqual(customJSONBDecoder.counter.load(ordering: .relaxed), 0) - PostgresNIO._defaultJSONDecoder = customJSONBDecoder - XCTAssertNoThrow(try PostgresData(json: Object(foo: 1, bar: 2)).json(as: Object.self)) - XCTAssertEqual(customJSONBDecoder.counter.load(ordering: .relaxed), 1) - } -}