Skip to content

Commit dfc290e

Browse files
authored
Fix bool interpolation (#7)
- add bool interpolation support - increase dep version - update readme - new tests to improve coverage - bump version
1 parent a80b9fa commit dfc290e

5 files changed

Lines changed: 250 additions & 7 deletions

File tree

Package.resolved

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ let package = Package(
3737
dependencies: [
3838
.package(url: "https://github.com/apple/swift-log", from: "1.6.0"),
3939
.package(url: "https://github.com/vapor/postgres-nio", from: "1.27.0"),
40-
.package(url: "https://github.com/feather-framework/feather-database", exact: "1.0.0-beta.4"),
40+
// .package(url: "https://github.com/feather-framework/feather-database", exact: "1.0.0-beta.4"),
41+
.package(url: "https://github.com/feather-framework/feather-database", branch: "main"),
4142
// [docc-plugin-placeholder]
4243
],
4344
targets: [

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
Postgres driver implementation for the abstract [Feather Database](https://github.com/feather-framework/feather-database) Swift API package.
44

55
[
6-
![Release: 1.0.0-beta.3](https://img.shields.io/badge/Release-1%2E0%2E0--beta%2E3-F05138)
6+
![Release: 1.0.0-beta.4](https://img.shields.io/badge/Release-1%2E0%2E0--beta%2E4-F05138)
77
](
8-
https://github.com/feather-framework/feather-postgres-database/releases/tag/1.0.0-beta.3
8+
https://github.com/feather-framework/feather-postgres-database/releases/tag/1.0.0-beta.4
99
)
1010

1111
## Features
@@ -37,7 +37,7 @@ Postgres driver implementation for the abstract [Feather Database](https://githu
3737
Add the dependency to your `Package.swift`:
3838

3939
```swift
40-
.package(url: "https://github.com/feather-framework/feather-postgres-database", exact: "1.0.0-beta.3"),
40+
.package(url: "https://github.com/feather-framework/feather-postgres-database", exact: "1.0.0-beta.4"),
4141
```
4242

4343
Then add `FeatherPostgresDatabase` to your target dependencies:

Sources/FeatherPostgresDatabase/PostgresDatabaseConnection.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ extension DatabaseQuery {
2222
.replacing("{{\(idx)}}", with: "$\(idx)")
2323

2424
switch binding.binding {
25+
case .bool(let value):
26+
postgresBindings.append(value)
2527
case .int(let value):
2628
postgresBindings.append(value)
2729
case .double(let value):

Tests/FeatherPostgresDatabaseTests/FeatherPostgresDatabaseTestSuite.swift

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,35 @@ struct FeatherPostgresDatabaseTestSuite {
792792
}
793793
}
794794

795+
@Test
796+
func transactionClosureErrorPropagates() async throws {
797+
try await runUsingTestDatabaseClient { database in
798+
enum TestError: Error, Equatable {
799+
case boom
800+
}
801+
802+
do {
803+
_ = try await database.withTransaction { _ in
804+
throw TestError.boom
805+
}
806+
Issue.record("Expected transaction error to be thrown.")
807+
}
808+
catch DatabaseError.transaction(let error) {
809+
#expect(error.beginError == nil)
810+
#expect(error.commitError == nil)
811+
#expect(error.rollbackError == nil)
812+
#expect((error.closureError as? TestError) == .boom)
813+
#expect(error.file.isEmpty == false)
814+
#expect(error.line > 0)
815+
}
816+
catch {
817+
Issue.record(
818+
"Expected database transaction error to be thrown."
819+
)
820+
}
821+
}
822+
}
823+
795824
@Test
796825
func concurrentTransactionUpdates() async throws {
797826
try await runUsingTestDatabaseClient { database in
@@ -1122,6 +1151,217 @@ struct FeatherPostgresDatabaseTestSuite {
11221151
}
11231152
}
11241153

1154+
@Test
1155+
func nullDecodingThrowsTypeMismatch() async throws {
1156+
try await runUsingTestDatabaseClient { database in
1157+
let suffix = randomTableSuffix()
1158+
let table = "nullable_values_\(suffix)"
1159+
1160+
try await database.withConnection { connection in
1161+
1162+
try await connection.run(
1163+
query: #"""
1164+
DROP TABLE IF EXISTS "\#(unescaped: table)" CASCADE;
1165+
"""#
1166+
)
1167+
try await connection.run(
1168+
query: #"""
1169+
CREATE TABLE "\#(unescaped: table)" (
1170+
"id" INTEGER NOT NULL PRIMARY KEY,
1171+
"value" INTEGER
1172+
);
1173+
"""#
1174+
)
1175+
1176+
try await connection.run(
1177+
query: #"""
1178+
INSERT INTO "\#(unescaped: table)"
1179+
("id", "value")
1180+
VALUES
1181+
(1, NULL);
1182+
"""#
1183+
)
1184+
1185+
let result =
1186+
try await connection.run(
1187+
query: #"""
1188+
SELECT "value"
1189+
FROM "\#(unescaped: table)";
1190+
"""#
1191+
) { try await $0.collect() }
1192+
1193+
#expect(result.count == 1)
1194+
1195+
do {
1196+
_ = try result[0].decode(column: "value", as: Int.self)
1197+
Issue.record("Expected decoding NULL as Int to throw.")
1198+
}
1199+
catch let DecodingError.typeMismatch(_, context) {
1200+
#expect(
1201+
context.debugDescription.contains(
1202+
"PostgresDecodingError"
1203+
)
1204+
)
1205+
}
1206+
catch {
1207+
Issue.record(
1208+
"Expected a typeMismatch error when decoding NULL as Int."
1209+
)
1210+
}
1211+
}
1212+
}
1213+
}
1214+
1215+
@Test
1216+
func nonPostgresDecodableTypeMismatch() async throws {
1217+
try await runUsingTestDatabaseClient { database in
1218+
let suffix = randomTableSuffix()
1219+
let table = "custom_types_\(suffix)"
1220+
1221+
struct CustomValue: Decodable, Sendable {
1222+
let value: String
1223+
}
1224+
1225+
try await database.withConnection { connection in
1226+
1227+
try await connection.run(
1228+
query: #"""
1229+
DROP TABLE IF EXISTS "\#(unescaped: table)" CASCADE;
1230+
"""#
1231+
)
1232+
try await connection.run(
1233+
query: #"""
1234+
CREATE TABLE "\#(unescaped: table)" (
1235+
"id" INTEGER NOT NULL PRIMARY KEY,
1236+
"value" TEXT NOT NULL
1237+
);
1238+
"""#
1239+
)
1240+
1241+
try await connection.run(
1242+
query: #"""
1243+
INSERT INTO "\#(unescaped: table)"
1244+
("id", "value")
1245+
VALUES
1246+
(1, 'alpha');
1247+
"""#
1248+
)
1249+
1250+
let result =
1251+
try await connection.run(
1252+
query: #"""
1253+
SELECT "value"
1254+
FROM "\#(unescaped: table)";
1255+
"""#
1256+
) { try await $0.collect() }
1257+
1258+
#expect(result.count == 1)
1259+
1260+
do {
1261+
_ = try result[0]
1262+
.decode(
1263+
column: "value",
1264+
as: CustomValue.self
1265+
)
1266+
Issue.record(
1267+
"Expected decoding non-PostgresDecodable type to throw."
1268+
)
1269+
}
1270+
catch let DecodingError.typeMismatch(_, context) {
1271+
#expect(
1272+
context.debugDescription.contains(
1273+
"Data is not convertible"
1274+
)
1275+
)
1276+
}
1277+
catch {
1278+
Issue.record(
1279+
"Expected a typeMismatch error for non-PostgresDecodable types."
1280+
)
1281+
}
1282+
}
1283+
}
1284+
}
1285+
1286+
@Test
1287+
func postgresRowDecodeMetatypeMismatch() async throws {
1288+
try await runUsingTestDatabaseClient { database in
1289+
let suffix = randomTableSuffix()
1290+
let table = "metatype_mismatch_\(suffix)"
1291+
1292+
func decodeWithMismatchedMetatype<T: Decodable, U: Decodable>(
1293+
row: DatabaseRow,
1294+
column: String,
1295+
as _: T.Type,
1296+
using _: U.Type
1297+
) throws -> T {
1298+
// Force a mismatched metatype to exercise the guard cast failure.
1299+
let mismatchedType = unsafeBitCast(U.self, to: T.Type.self)
1300+
return try row.decode(column: column, as: mismatchedType)
1301+
}
1302+
1303+
try await database.withConnection { connection in
1304+
1305+
try await connection.run(
1306+
query: #"""
1307+
DROP TABLE IF EXISTS "\#(unescaped: table)" CASCADE;
1308+
"""#
1309+
)
1310+
try await connection.run(
1311+
query: #"""
1312+
CREATE TABLE "\#(unescaped: table)" (
1313+
"id" INTEGER NOT NULL PRIMARY KEY,
1314+
"value" TEXT NOT NULL
1315+
);
1316+
"""#
1317+
)
1318+
1319+
try await connection.run(
1320+
query: #"""
1321+
INSERT INTO "\#(unescaped: table)"
1322+
("id", "value")
1323+
VALUES
1324+
(1, 'abc');
1325+
"""#
1326+
)
1327+
1328+
let result =
1329+
try await connection.run(
1330+
query: #"""
1331+
SELECT "value"
1332+
FROM "\#(unescaped: table)";
1333+
"""#
1334+
) { try await $0.collect() }
1335+
1336+
#expect(result.count == 1)
1337+
1338+
do {
1339+
_ = try decodeWithMismatchedMetatype(
1340+
row: result[0],
1341+
column: "value",
1342+
as: Int.self,
1343+
using: String.self
1344+
)
1345+
Issue.record(
1346+
"Expected mismatched metatype decode to throw."
1347+
)
1348+
}
1349+
catch let DecodingError.typeMismatch(_, context) {
1350+
#expect(
1351+
context.debugDescription.contains(
1352+
"Could not convert data"
1353+
)
1354+
)
1355+
}
1356+
catch {
1357+
Issue.record(
1358+
"Expected a typeMismatch error for metatype mismatch."
1359+
)
1360+
}
1361+
}
1362+
}
1363+
}
1364+
11251365
@Test
11261366
func queryFailureErrorText() async throws {
11271367
try await runUsingTestDatabaseClient { database in

0 commit comments

Comments
 (0)