Skip to content

Commit

Permalink
Fix transaction rollback with top-level throw (#90)
Browse files Browse the repository at this point in the history
* Fix GH89 by adding catchFlatMap to catch transaction closure throws. Add test.

* Add tests to allTests

* Update the contribute script to use docker-compose instead of docker-machine.

Co-authored-by: Tanner <[email protected]>
  • Loading branch information
clayellis and tanner0101 authored Jun 13, 2020
1 parent c00c474 commit a3d6587
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
extension PostgreSQLDatabase: TransactionSupporting {
/// See `TransactionSupporting`.
public static func transactionExecute<T>(_ transaction: @escaping (PostgreSQLConnection) throws -> Future<T>, on connection: PostgreSQLConnection) -> Future<T> {
func rollback(error: Error) -> Future<T> {
return connection.simpleQuery("ROLLBACK").map { throw error }
}

return connection.simpleQuery("BEGIN TRANSACTION").flatMap { results in
return try transaction(connection).flatMap { res in
return connection.simpleQuery("END TRANSACTION").transform(to: res)
}.catchFlatMap { error in
return connection.simpleQuery("ROLLBACK").map { results in
throw error
}
}
}
}.catchFlatMap(rollback)
}.catchFlatMap(rollback)
}
}
43 changes: 42 additions & 1 deletion Tests/FluentPostgreSQLTests/FluentPostgreSQLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,45 @@ class FluentPostgreSQLTests: XCTestCase {
try C.prepare(on: conn).wait()
defer { try? C.revert(on: conn).wait() }
}

// https://github.com/vapor/fluent-postgresql/issues/89
func testGH89() throws {
let conn = try benchmarker.pool.requestConnection().wait()
conn.logger = DatabaseLogger(database: .psql, handler: PrintLogHandler())
defer { benchmarker.pool.releaseConnection(conn) }

try Planet.prepare(on: conn).wait()
defer { try? Planet.revert(on: conn).wait() }

enum SomeError: Error {
case error
}

func alwaysThrows() throws {
throw SomeError.error
}

var a = Planet(name: "Pluto")
a = try a.save(on: conn).wait()

do {
_ = try conn.transaction(on: .psql) { transaction -> Future<Planet> in
a.name = "No Longer A Planet"
let save = a.save(on: transaction)
try alwaysThrows()
return save
}.wait()
} catch {
// No-op
}

a = try Planet.query(on: conn)
.filter(\.id == a.requireID())
.first()
.wait()!

XCTAssertEqual(a.name, "Pluto")
}

// https://github.com/vapor/fluent-postgresql/issues/85
func testGH85() throws {
Expand Down Expand Up @@ -555,8 +594,10 @@ class FluentPostgreSQLTests: XCTestCase {
("testCustomFilter", testCustomFilter),
("testCreateOrUpdate", testCreateOrUpdate),
("testEnumArray", testEnumArray),
("testAlterDrop", testAlterDrop),
("testGH89", testGH89),
("testGH85", testGH85),
("testGH35", testGH35),
("testGH35", testGH35)
]
}

Expand Down
23 changes: 4 additions & 19 deletions contribute_boostrap.sh
Original file line number Diff line number Diff line change
@@ -1,23 +1,8 @@
echo "πŸ’§ starting docker..."
docker-machine start default

echo "πŸ’§ exporting docker machine environment..."
eval $(docker-machine env default)

echo "πŸ’§ cleaning previous vapor-psql dev db..."
docker stop vapor-psql
docker rm vapor-psql

echo "πŸ’§ creating vapor-psql dev db..."
docker run --name vapor-psql -e POSTGRES_USER=vapor_username -e POSTGRES_DB=vapor_database -p 5432:5432 -d postgres:latest

echo "πŸ’§ generating xcode proj..."
swift package generate-xcodeproj

echo "πŸ’§ add the following env variable to Xcode test scheme:"
echo ""
echo " PSQL_HOSTNAME: `docker-machine ip`"
echo ""

echo "πŸ’§ opening xcode..."
open *.xcodeproj
open *.xcodeproj

echo "πŸ’§ starting docker..."
docker-compose up psql-10

0 comments on commit a3d6587

Please sign in to comment.