Skip to content

Commit 50945aa

Browse files
authored
Merge pull request #78 from vapor/transactions
Added Transaction to connection
2 parents be6cfa6 + df385a5 commit 50945aa

File tree

3 files changed

+85
-3
lines changed

3 files changed

+85
-3
lines changed

Sources/MySQL/Connection.swift

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,31 @@ public final class Connection {
4646

4747
mysql_set_character_set(cConnection, encoding)
4848
}
49-
49+
50+
public func transaction(_ closure: () throws -> Void) throws {
51+
// required by transactions, but I don't want to open the old
52+
// MySQL query API to the public as it would be a burden to maintain.
53+
func oldQuery(_ query: String) throws {
54+
try lock.locked {
55+
guard mysql_query(cConnection, query) == 0 else {
56+
throw Error.execute(error)
57+
}
58+
}
59+
}
60+
61+
try oldQuery("START TRANSACTION")
62+
63+
do {
64+
try closure()
65+
} catch {
66+
// rollback changes and then rethrow the error
67+
try oldQuery("ROLLBACK")
68+
throw error
69+
}
70+
71+
try oldQuery("COMMIT")
72+
}
73+
5074
@discardableResult
5175
public func execute(_ query: String, _ values: [NodeRepresentable] = []) throws -> [[String: Node]] {
5276
var returnable: [[String: Node]] = []

Tests/LinuxMain.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import XCTest
44
@testable import MySQLTests
55

66
XCTMain([
7-
testCase(MySQLTests.allTests)
7+
testCase(DateTests.allTests),
8+
testCase(MySQLTests.allTests),
89
])
910

10-
#endif
11+
#endif

Tests/MySQLTests/MySQLTests.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ class MySQLTests: XCTestCase {
1111
("testTimestamps", testTimestamps),
1212
("testSpam", testSpam),
1313
("testError", testError),
14+
("testTransaction", testTransaction),
15+
("testTransactionFailed", testTransactionFailed),
1416
]
1517

1618
var mysql: MySQL.Database!
@@ -201,4 +203,59 @@ class MySQLTests: XCTestCase {
201203
XCTFail("Wrong error: \(error)")
202204
}
203205
}
206+
207+
func testTransaction() {
208+
do {
209+
let c = try mysql.makeConnection()
210+
try c.execute("DROP TABLE IF EXISTS transaction")
211+
try c.execute("CREATE TABLE transaction (name VARCHAR(64))")
212+
try c.execute("INSERT INTO transaction VALUES (?)", [
213+
"james"
214+
])
215+
216+
try c.transaction {
217+
try c.execute("UPDATE transaction SET name = 'James' where name = 'james'")
218+
}
219+
220+
if let james = try c.execute("SELECT * FROM transaction").first {
221+
XCTAssertEqual(james["name"]?.string, "James")
222+
} else {
223+
XCTFail("There should be one entry.")
224+
}
225+
} catch {
226+
XCTFail("Testing transaction failed: \(error)")
227+
}
228+
}
229+
230+
func testTransactionFailed() {
231+
do {
232+
let c = try mysql.makeConnection()
233+
try c.execute("DROP TABLE IF EXISTS transaction")
234+
try c.execute("CREATE TABLE transaction (name VARCHAR(64))")
235+
try c.execute("INSERT INTO transaction VALUES (?)", [
236+
"tommy"
237+
])
238+
239+
do {
240+
try c.transaction {
241+
// will succeed, but will be rolled back
242+
try c.execute("UPDATE transaction SET name = 'Timmy'")
243+
244+
// malformed query, will throw
245+
try c.execute("💉")
246+
}
247+
248+
XCTFail("Transaction should have rethrown error.")
249+
250+
} catch {
251+
if let tommy = try c.execute("SELECT * FROM transaction").first {
252+
XCTAssertEqual(tommy["name"]?.string, "tommy", "Should have ROLLBACK")
253+
} else {
254+
XCTFail("There should be one entry.")
255+
}
256+
}
257+
} catch {
258+
XCTFail("Testing transaction failed: \(error)")
259+
}
260+
}
204261
}

0 commit comments

Comments
 (0)