From 0c27d1f8f370ecd07acd7aae80f74a9dd1c797f2 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Tue, 22 Apr 2025 16:44:08 +0200 Subject: [PATCH 1/2] Ensure ltree works --- .../New/Data/String+PostgresCodable.swift | 9 ++- .../PostgresClientTests.swift | 55 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/Sources/PostgresNIO/New/Data/String+PostgresCodable.swift b/Sources/PostgresNIO/New/Data/String+PostgresCodable.swift index 41091ab3..6bd09e78 100644 --- a/Sources/PostgresNIO/New/Data/String+PostgresCodable.swift +++ b/Sources/PostgresNIO/New/Data/String+PostgresCodable.swift @@ -36,13 +36,20 @@ extension String: PostgresDecodable { // we can force unwrap here, since this method only fails if there are not enough // bytes available. self = buffer.readString(length: buffer.readableBytes)! + case (_, .uuid): guard let uuid = try? UUID(from: &buffer, type: .uuid, format: format, context: context) else { throw PostgresDecodingError.Code.failure } self = uuid.uuidString + default: - throw PostgresDecodingError.Code.typeMismatch + // We should eagerly try to convert any datatype into a String. For example the oid + // for ltree isn't static. For this reason we should just try to convert anything. + guard let string = buffer.readString(length: buffer.readableBytes) else { + throw PostgresDecodingError.Code.typeMismatch + } + self = string } } } diff --git a/Tests/IntegrationTests/PostgresClientTests.swift b/Tests/IntegrationTests/PostgresClientTests.swift index 34a8ad2a..eaf3663f 100644 --- a/Tests/IntegrationTests/PostgresClientTests.swift +++ b/Tests/IntegrationTests/PostgresClientTests.swift @@ -292,6 +292,61 @@ final class PostgresClientTests: XCTestCase { XCTFail("Unexpected error: \(String(reflecting: error))") } } + + func testLTree() async throws { + let tableName = "test_client_ltree" + + var mlogger = Logger(label: "test") + mlogger.logLevel = .debug + let logger = mlogger + let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 8) + self.addTeardownBlock { + try await eventLoopGroup.shutdownGracefully() + } + + let clientConfig = PostgresClient.Configuration.makeTestConfiguration() + let client = PostgresClient(configuration: clientConfig, eventLoopGroup: eventLoopGroup, backgroundLogger: logger) + + try await withThrowingTaskGroup(of: Void.self) { taskGroup in + taskGroup.addTask { + await client.run() + } + + try await client.query("CREATE EXTENSION IF NOT EXISTS ltree;") + + try await client.query("DROP TABLE IF EXISTS \"\(unescaped: tableName)\";") + + try await client.query( + """ + CREATE TABLE IF NOT EXISTS "\(unescaped: tableName)" ( + id SERIAL PRIMARY KEY, + label ltree NOT NULL + ); + """ + ) + + try await client.query( + """ + INSERT INTO "\(unescaped: tableName)" (label) VALUES ('foo.bar.baz') + """ + ) + + let rows = try await client.query( + """ + SELECT id, label FROM "\(unescaped: tableName)" WHERE label ~ 'foo.*' + """ + ) + + var count = 0 + for try await (id, label) in rows.decode((Int, String).self) { + count += 1 + } + XCTAssertEqual(count, 1) + + taskGroup.cancelAll() + } + } + } @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) From 47dcdf2518768fc6839f63a0e577f958c8591521 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Tue, 22 Apr 2025 17:53:50 +0200 Subject: [PATCH 2/2] Fix unit tests --- .../New/Data/String+PSQLCodableTests.swift | 12 ------------ Tests/PostgresNIOTests/New/PostgresRowTests.swift | 8 ++++---- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/Tests/PostgresNIOTests/New/Data/String+PSQLCodableTests.swift b/Tests/PostgresNIOTests/New/Data/String+PSQLCodableTests.swift index 6ff35130..aadeabff 100644 --- a/Tests/PostgresNIOTests/New/Data/String+PSQLCodableTests.swift +++ b/Tests/PostgresNIOTests/New/Data/String+PSQLCodableTests.swift @@ -31,18 +31,6 @@ class String_PSQLCodableTests: XCTestCase { } } - func testDecodeFailureFromInvalidType() { - let buffer = ByteBuffer() - let dataTypes: [PostgresDataType] = [.bool, .float4Array, .float8Array] - - for dataType in dataTypes { - var loopBuffer = buffer - XCTAssertThrowsError(try String(from: &loopBuffer, type: dataType, format: .binary, context: .default)) { - XCTAssertEqual($0 as? PostgresDecodingError.Code, .typeMismatch) - } - } - } - func testDecodeFromUUID() { let uuid = UUID() var buffer = ByteBuffer() diff --git a/Tests/PostgresNIOTests/New/PostgresRowTests.swift b/Tests/PostgresNIOTests/New/PostgresRowTests.swift index 7aa4c7e6..4a34e3b0 100644 --- a/Tests/PostgresNIOTests/New/PostgresRowTests.swift +++ b/Tests/PostgresNIOTests/New/PostgresRowTests.swift @@ -172,7 +172,7 @@ final class PostgresRowTests: XCTestCase { name: "name", tableOID: 1, columnAttributeNumber: 1, - dataType: .int8, + dataType: .text, dataTypeSize: 0, dataTypeModifier: 0, format: .binary @@ -185,7 +185,7 @@ final class PostgresRowTests: XCTestCase { columns: rowDescription ) - XCTAssertThrowsError(try row.decode((UUID?, String).self)) { error in + XCTAssertThrowsError(try row.decode((UUID?, Int).self)) { error in guard let psqlError = error as? PostgresDecodingError else { return XCTFail("Unexpected error type") } XCTAssertEqual(psqlError.columnName, "name") @@ -194,8 +194,8 @@ final class PostgresRowTests: XCTestCase { XCTAssertEqual(psqlError.file, #fileID) XCTAssertEqual(psqlError.postgresData, ByteBuffer(integer: 123)) XCTAssertEqual(psqlError.postgresFormat, .binary) - XCTAssertEqual(psqlError.postgresType, .int8) - XCTAssert(psqlError.targetType == String.self) + XCTAssertEqual(psqlError.postgresType, .text) + XCTAssert(psqlError.targetType == Int.self) } } }