Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add ORDER BY to query builder #9

Merged
merged 1 commit into from
Sep 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions Sources/MySQL/MySQLQueryBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public func ==(lhs: MySQLQueryBuilder, rhs: MySQLQueryBuilder) -> Bool {
}

public enum MySQLFunction {
case LastInsertID
case LastInsertID
}

public enum Joins {
Expand All @@ -14,6 +14,11 @@ public enum Joins {
case InnerJoin
}

public enum Orders: String {
case Ascending = "ASC"
case Descending = "DESC"
}

internal struct MySQLJoin {
let from: String
let to: String
Expand All @@ -34,6 +39,7 @@ public class MySQLQueryBuilder: Equatable {
var updateStatement: String?
var deleteStatement: String?
var whereStatement: String?
var orderStatement: String?
var upsertStatement: String?

var joinedStatements = [MySQLJoin]()
Expand Down Expand Up @@ -78,7 +84,7 @@ public class MySQLQueryBuilder: Equatable {
public func select(fields: [Any], table: String) -> MySQLQueryBuilder {
self.fields = fields
self.tableName = table

return self
}

Expand Down Expand Up @@ -120,7 +126,7 @@ public class MySQLQueryBuilder: Equatable {

return self
}

/**
upsert updates a record if it exists and inserts a new one if it does not
exist
Expand Down Expand Up @@ -214,6 +220,11 @@ public class MySQLQueryBuilder: Equatable {
return self
}

public func order(byExpression expression: String, order: Orders = .Ascending) -> MySQLQueryBuilder {
orderStatement = " ORDER BY \(expression) \(order.rawValue)"
return self
}

private func escapeParameter(_ parameter: Any) -> String {
switch parameter {
case is Int:
Expand Down Expand Up @@ -284,7 +295,7 @@ public class MySQLQueryBuilder: Equatable {
if let updateStatement = updateStatement {
query += updateStatement
}

if let upsertStatement = upsertStatement {
query += upsertStatement
}
Expand All @@ -297,6 +308,10 @@ public class MySQLQueryBuilder: Equatable {
query += whereStatement
}

if let orderStatement = orderStatement {
query += orderStatement
}

return query + ";"
}

Expand All @@ -321,11 +336,11 @@ public class MySQLQueryBuilder: Equatable {
statement += "\(join.builder.tableName!).\(field), "
}
}

statement = statement.trimChar(character: " ")
statement = statement.trimChar(character: ",")
statement += " FROM \(table)"

for join in joins {
switch join.type {
case .LeftJoin:
Expand All @@ -338,7 +353,7 @@ public class MySQLQueryBuilder: Equatable {

statement += "\(table).\(join.from) = \(join.builder.tableName!).\(join.to)"
}

statement = statement.trimChar(character: " ")
statement = statement.trimChar(character: ",")

Expand Down Expand Up @@ -369,7 +384,7 @@ public class MySQLQueryBuilder: Equatable {

private func createUpsertStatement(data: MySQLRow, table: String) -> String {
let update = createInsertStatement(data: data, table: table)

var statement = " ON DUPLICATE KEY UPDATE "
for (key, value) in data {
statement += "\(key) = '\(value)', "
Expand All @@ -394,7 +409,7 @@ public class MySQLQueryBuilder: Equatable {
}

private func createDeleteStatement(withTable table: String) -> String {
return "DELETE FROM \(table)"
return "DELETE FROM \(table)"
}
}

Expand Down
49 changes: 34 additions & 15 deletions Tests/IntegrationTests/IntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class IntegrationTests: XCTestCase {
func createConnection(
connectionString: MySQLConnectionString,
block: ((MySQLConnectionProtocol) throws -> Void)) {

var pool = MySQLConnectionPool(connectionString: connectionString, poolSize: 1, defaultCharset: "utf8")

do {
Expand All @@ -31,7 +31,7 @@ public class IntegrationTests: XCTestCase {

try block(connection)
} catch {
print(error)
print(error)
XCTFail("An exception has ocurred: \(error)")
}
}
Expand Down Expand Up @@ -84,7 +84,7 @@ public class IntegrationTests: XCTestCase {
.select(fields: ["Id", "Name", "Price", "UpdatedAt"], table: "Cars")

let result = try connection.execute(builder: queryBuilder)

if let r = result.nextResult() {
XCTAssertEqual(1, r["Id"] as! Int)
XCTAssertEqual("Audi", r["Name"] as! String)
Expand Down Expand Up @@ -112,15 +112,34 @@ public class IntegrationTests: XCTestCase {
XCTFail("No results")
}

while case let row? = result.nextResult() {
while case let row? = result.nextResult() {
XCTAssertNotNil(row)
rowCount += 1
}
}

XCTAssertEqual(2, rowCount)
}


func testSelectWithOrderedResults() {
connectionString!.database = "testdb"
createConnection(connectionString: connectionString!) {
(connection: MySQLConnectionProtocol) in

let queryBuilder = MySQLQueryBuilder()
.select(fields: ["Id", "Name", "Price", "UpdatedAt"], table: "Cars")
.order(byExpression: "Name", order: .Descending)

let result = try connection.execute(builder: queryBuilder)

if let r = result.nextResult() {
XCTAssertEqual("Mercedes", r["Name"] as! String)
} else {
XCTFail("No results")
}
}
}

func testUpdateWithNoRecordFails() {
connectionString!.database = "testdb"
createConnection(connectionString: connectionString!) {
Expand All @@ -133,7 +152,7 @@ public class IntegrationTests: XCTestCase {
.wheres(statement: "id=?", parameters: "12")

let result = try connection.execute(builder: queryBuilder)

XCTAssertEqual(0, result.affectedRows)
}
}
Expand Down Expand Up @@ -173,11 +192,11 @@ public class IntegrationTests: XCTestCase {
.upsert(data: row, table: "Cars")

let result = try connection.execute(builder: queryBuilder)

XCTAssertEqual(1, result.affectedRows)
}
}

func testUpsertWithRecordUpdates() {
connectionString!.database = "testdb"
createConnection(connectionString: connectionString!) {
Expand All @@ -194,7 +213,7 @@ public class IntegrationTests: XCTestCase {

let selectBuilder = MySQLQueryBuilder()
.select(fields: ["Id", "Name"], table: "Cars")
.wheres(statement: "Id = ?", parameters: "7")
.wheres(statement: "Id = ?", parameters: "7")
let selectResult = try connection.execute(builder: selectBuilder)

guard let data = selectResult.nextResult() else {
Expand All @@ -205,7 +224,7 @@ public class IntegrationTests: XCTestCase {
XCTAssertEqual("Car B", data["Name"] as? String)
}
}

func testInsertAndRollbackDoesNotCreateRecord() {
connectionString!.database = "testdb"
createConnection(connectionString: connectionString!) {
Expand All @@ -226,16 +245,16 @@ public class IntegrationTests: XCTestCase {

let selectBuilder = MySQLQueryBuilder()
.select(fields: ["Id", "Name"], table: "Cars")
.wheres(statement: "Id = ?", parameters: "10")
.wheres(statement: "Id = ?", parameters: "10")
let selectResult = try connection.execute(builder: selectBuilder)

if let _ = selectResult.nextResult() {
if let _ = selectResult.nextResult() {
XCTFail("Transaction should have been rolled back")
return
}
}
}

func testInsertAndCommitCreatesRecord() {
connectionString!.database = "testdb"
createConnection(connectionString: connectionString!) {
Expand All @@ -256,10 +275,10 @@ public class IntegrationTests: XCTestCase {

let selectBuilder = MySQLQueryBuilder()
.select(fields: ["Id", "Name"], table: "Cars")
.wheres(statement: "Id = ?", parameters: "11")
.wheres(statement: "Id = ?", parameters: "11")
let selectResult = try connection.execute(builder: selectBuilder)

guard let data = selectResult.nextResult() else {
guard let data = selectResult.nextResult() else {
XCTFail("No data")
return
}
Expand Down
50 changes: 38 additions & 12 deletions Tests/MySQLTests/MySQLQueryBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ public class MySQLQueryBuilderTests : XCTestCase {
XCTAssertEqual(builder, ret, "Should have returned self")
}

public func testOrderReturnsSelf() {
let builder = MySQLQueryBuilder()
let ret = builder.order(byExpression: "id", order: .Ascending)

XCTAssertEqual(builder, ret, "Should have returned self")
}

public func testSelectReturnsValidQuery() {
let builder = MySQLQueryBuilder()
let statement = builder
Expand All @@ -63,7 +70,7 @@ public class MySQLQueryBuilderTests : XCTestCase {

XCTAssertEqual("SELECT MyTABLE.Field1, MyTABLE.Field2 FROM MyTABLE;", statement, "Returned invalid select statement")
}

public func testSelectWithArrayContainingFunctionReturnsValidQuery() {
let builder = MySQLQueryBuilder()
let statement = builder
Expand Down Expand Up @@ -96,7 +103,7 @@ public class MySQLQueryBuilderTests : XCTestCase {

XCTAssertEqual("UPDATE MyTable SET abc='abc', bcd='bcd';", query, "Should have returned valid query")
}

public func testUpsertGeneratesValidQuery() {
var data = MySQLRow()
data["abc"] = "abc"
Expand All @@ -105,8 +112,8 @@ public class MySQLQueryBuilderTests : XCTestCase {
let query = MySQLQueryBuilder()
.upsert(data: data, table: "MyTable")
.build()
XCTAssertEqual("INSERT INTO MyTable (abc, bcd) VALUES ('abc', 'bcd') ON DUPLICATE KEY UPDATE abc = 'abc', bcd = 'bcd';",

XCTAssertEqual("INSERT INTO MyTable (abc, bcd) VALUES ('abc', 'bcd') ON DUPLICATE KEY UPDATE abc = 'abc', bcd = 'bcd';",
query,
"Should have returned valid query")
}
Expand All @@ -127,31 +134,47 @@ public class MySQLQueryBuilderTests : XCTestCase {

XCTAssertEqual(" WHERE param1='abc' and param2=10;", query, "Should have returned valid query")
}

public func testWheresGeneratesValidQueryWithFunctionParameters() {
let query = MySQLQueryBuilder()
.wheres(statement: "param1=? and param2=?", parameters: "abc", MySQLFunction.LastInsertID)
.build()

XCTAssertEqual(" WHERE param1='abc' and param2=LAST_INSERT_ID();", query, "Should have returned valid query")
}

public func testWheresGeneratesValidQueryWithArrayParameters() {
let query = MySQLQueryBuilder()
.wheres(statement: "id IN(?)", parameters: ["abc", 123])
.wheres(statement: "id IN(?)", parameters: ["abc", 123])
.build()

XCTAssertEqual(" WHERE id IN('abc', 123);", query, "Should have returned valid query")
}

public func testWheresGeneratesValidQueryWithMixedParameters() {
let query = MySQLQueryBuilder()
.wheres(statement: "name=? AND id IN(?)", parameters: "nic", ["abc", 123])
.wheres(statement: "name=? AND id IN(?)", parameters: "nic", ["abc", 123])
.build()

XCTAssertEqual(" WHERE name='nic' AND id IN('abc', 123);", query, "Should have returned valid query")
}

public func testOrderGeneratesValidQuery() {
let query = MySQLQueryBuilder()
.order(byExpression: "id", order: .Ascending)
.build()

XCTAssertEqual(" ORDER BY id ASC;", query, "Should have returned valid query")
}

public func testOrderDescendingGeneratesValidQuery() {
let query = MySQLQueryBuilder()
.order(byExpression: "name", order: .Descending)
.build()

XCTAssertEqual(" ORDER BY name DESC;", query, "Should have returned valid query")
}

public func testSelectWithWheresGeneratesValidQuery() {
let query = MySQLQueryBuilder()
.select(statement: "SELECT * FROM TABLE")
Expand All @@ -160,15 +183,15 @@ public class MySQLQueryBuilderTests : XCTestCase {

XCTAssertEqual("SELECT * FROM TABLE WHERE param1='abc' and param2='bcd';", query, "Should have returned valid query")
}

public func testSelectWithWheresAndFieldsGeneratesValidQuery() {
let query = MySQLQueryBuilder()
.select(fields: ["Field1", "Field2"], table: "MyTable")
.wheres(statement: "Field1=? and Field2=?", parameters: "abc", "bcd")
.build()

XCTAssertEqual("SELECT MyTable.Field1, MyTable.Field2 FROM MyTable WHERE MyTable.Field1='abc' and MyTable.Field2='bcd';",
query,
XCTAssertEqual("SELECT MyTable.Field1, MyTable.Field2 FROM MyTable WHERE MyTable.Field1='abc' and MyTable.Field2='bcd';",
query,
"Should have returned valid query")
}

Expand Down Expand Up @@ -202,7 +225,7 @@ public class MySQLQueryBuilderTests : XCTestCase {

XCTAssertEqual("SELECT MyTable1.Field1, MyTable1.Field2, MyTable2.Id FROM MyTable1 " +
"INNER JOIN MyTable2 ON MyTable1.Field1 = MyTable2.Id;",
query,
query,
"Should have returned valid query")
}

Expand All @@ -228,6 +251,7 @@ extension MySQLQueryBuilderTests {
("testSelectReturnsSelf", testSelectReturnsSelf),
("testSelectWithFieldsReturnsSelf", testSelectWithFieldsReturnsSelf),
("testWheresReturnsSelf", testWheresReturnsSelf),
("testOrderReturnsSelf", testOrderReturnsSelf),
("testSelectReturnsValidQuery", testSelectReturnsValidQuery),
("testSelectWithArrayReturnsValidQuery", testSelectWithArrayReturnsValidQuery),
("testSelectWithArrayContainingFunctionReturnsValidQuery", testSelectWithArrayContainingFunctionReturnsValidQuery),
Expand All @@ -239,6 +263,8 @@ extension MySQLQueryBuilderTests {
("testWheresGeneratesValidQueryWithFunctionParameters", testWheresGeneratesValidQueryWithFunctionParameters),
("testWheresGeneratesValidQueryWithArrayParameters", testWheresGeneratesValidQueryWithArrayParameters),
("testWheresGeneratesValidQueryWithMixedParameters", testWheresGeneratesValidQueryWithMixedParameters),
("testOrderGeneratesValidQuery", testOrderGeneratesValidQuery),
("testOrderDescendingGeneratesValidQuery", testOrderDescendingGeneratesValidQuery),
("testSelectWithWheresGeneratesValidQuery", testSelectWithWheresGeneratesValidQuery),
("testUpdateWithWheresGeneratesValidQuery", testUpdateWithWheresGeneratesValidQuery),
("testDeleteWithWheresGeneratesValidQuery", testDeleteWithWheresGeneratesValidQuery),
Expand Down