A Mysql Native Client and Query Builder that works on Mac and Linux.
This library is powerded by Prorsum
SwiftKnex is aimed to use at production services at my company
- Pure Swift Implementation(doesn't depend on libmysqlclient)
- Expressive querying
- Supporting Database Migration and Rollback
- Supported Mysql 5.7 JSON Data Type
- Async I/O
- BlobType
- GeoType
- Investigate Performance
- Async I/O Mode
- Documentation
All developers should feel welcome and encouraged to contribute to SwiftKnex.
To contribute a feature or idea to SwiftKnex please submit an issue! If you find bugs, of course you can create the PR(s) directory.
let config = KnexConfig(
host: "localhost",
user: "user",
database: "my_database",
isShowSQLLog: true
)
let con = try KnexConnection(config: config)
let knex = con.knex()
let results = try knex.table("users").where("id" == 1).fetch()
print(results)
You can chain the conditional clauses like SQL and it's amazing expressive.
let results = try knex
.table("users")
.where(between("id", 1, 1000))
.limit(10)
.offset(10)
.fetch()
print(result)
let results = try knex
.table("users")
.join("payments")
.on("users.id" == "payment.user_id")
.where("users.id" == 1)
.limit(10)
.offset(10)
.fetch()
print(results)
let results = try knex
.select(count("id").as("count"))
.table("users")
.fetch()
print(results)
let results = try knex
.table("users")
.where(("country" == "Japan" && "age" > 20) || "country" == "USA")
print(results)
let results = try knex
.table(
Table(
QueryBuilder()
.table("users")
.where("country" == "USA")
)
).as("t1")
)
.where("t1.id" == 1)
print(results)
let results = try knex
.table("users")
.where(SwiftKnex.in("id",
QueryBuilder()
.select(col("id"))
.table("t1")
.where("country" == "USA")
)).fetch()
print(results)
SwiftKnex can evaluate comparison formula as function.
If you input "id" == 1
in where
clause, it is evaluated as function
(Not BOOL) and tnrasform to SQL Comparison Literal.
SwiftKnex | Mysql |
---|---|
where("id" == 1) | where id = 1 |
where("id" > 1) | where id > 1 |
where("id" >= 1) | where id >= 1 |
where("id" < 1) | where id < 1 |
where("id" <= 1) | where id <= 1 |
where("id" != 1) | where id != 1 |
SwiftKnex provides conditions as function. Here are list of conditions available now.
where(_ filter: ConditionFilter)
- like:
knex().where(like("name", "%a%"))
- in:
knex().where(in("id", [1, 2, 3]))
- notIn:
knex().where(notIn("id", [1, 2, 3]))
- between:
knex().where(between("date", "2017-01-01", "2017-01-31"))
- notBetween:
knex().where(notBetween("date", "2017-01-01", "2017-01-31"))
- isNull:
knex().where(isNull("deleted_at"))
- isNotNull:
knex().where(isNotNull("deleted_at"))
- raw:
knex().where(raw("id = ?", [1]))
- like:
or(_ filter: ConditionFilter)
knex().where(in("id", [1, 2, 3])).or(in("id", [4, 5, 6]))
Of cource it supports other clauses. You can use them with knex()
's method chain.
- join(_ table: String):
knex().table("a").join("b")
- leftJoin(_ table: String):
knex().table("a").leftJoin("b")
- rightJoin(_ table: String):
knex().table("a").rightJoin("b")
- innerJoin(_ with table: String):
knex().table("a").innerJoin("b")
- on(_ filter: ConditionFilter):
knex().table("a").join("b").on("a.id" == "b.a_id")
- limit(_ limit: Int):
knex().limit(10)
- offset(_ offset: Int):
knex().limit(10).offset(100)
- order(by: String, sort: OrderSort = .asc):
knex().order(by: "created_at", .desc)
- group(by name: String):
knex().group(by: "company")
- having(_ cond: ConditionFilter):
knex().group(by: "company").having(in("name", ["Google", "Apple"]))
Define Your Entity with confirming Entity
protocol and fetch rows as your specified type.
struct User: Entity, Serializable {
let id: Int
let name: String
let email: String
let age: Int
let country: String?
init(row: Row) throws {
self.id = row["id"] as! Int
self.name = row["name"] as! String
self.email = row["email"] as! String
self.age = row["age"] as! Int
self.country = row["country"] as? String
}
func serialize() throws -> [String: Any] {
return [
"name": name,
"email": email,
"age": age,
"country": country
]
}
}
// fetch rows as User(s)
let users: [User] = try! con.knex()
.table("users")
.where("country" == "Japan")
.fetch()
print(users.first)
// Insert User(should confirm Serializable)
let result = try! con.knex().insert(into: "users", values: user)
print(result?.insertId)
let result = try knex().insert(into: "users", values: ["id": 1, "name": "bar"])
print(result.affectedRows)
let result = try knex().table("users").where("id" == 1).update(sets: ["name": "foobar"])
print(result.affectedRows)
let result = try knex().table("users").where("id" == 1).delete()
print(result.affectedRows)
do {
try con.knex().transaction { trx in // BEGIN TRANSCTION
try con.knex().table("users").where("id" == 1).update(sets: ["name": "foo"], trx: trx)
try con.knex().table("users").where("id" == 2).update(sets: ["name": "bar"], trx: trx)
try con.knex().table("users").where("id" == 3).update(sets: ["name": "foobar"], trx: trx)
}
// COMMIT
} catch {
// ROLLBACK
}
let create = Create(table: "users", fields: [
Schema.integer("id").asPrimaryKey().asAutoIncrement(),
Schema.string("name"),
Schema.string("email").asUnique(),
Schema.datetime("last_logined_at").asIndex()
])
.hasTimeStamps() // add created_at and updated_at
try knex().execRaw(sql: create.toDDL())
SwiftKnex | Mysql Type |
---|---|
Schema.string | VARCHAR |
Schema.integer | INT |
Schema.bigInteger | BIGIMT |
Schema.dateTime | DATETIME |
Schema.text | TEXT |
Schema.mediumText | MEDIUMTEXT |
Schema.float | FLOAT |
Schema.double | DOUBLE |
Schema.boolean | TINYINT(1) |
Schema.json | JSON |
default(as value: Any)
after(for name: String)
asPrimaryKey()
asAutoIncrement()
asNotNullable()
asUnsigned()
charset(_ char: Charset)
asUnique()
asIndex()
let drop = Drop(table: "users")
try knex().execRaw(sql: drop.toDDL())
You can perform raw sql with SwiftKnex
try knex().execRaw(sql: "SELECT * from users where id = ?", params: [1])
SwiftKnex supports database migration and rollback features.
Package.swift
import PackageDescription
let package = Package(
name: "MyApp",
dependencies: [
.Package(url: "https://github.com/noppoMan/SwiftKnex.git", majorVersion: 0, minor: 1)
]
)
run swift build
$ swift build
and then, SwiftKnexMigration
executable binary was created in the .build/debug directory.
the next step is creating migration class file into your Sources/Migration
directory with ./build/debug/Migration create {ResourceName}
here is an example for creating CreateUser
migration file
.build/debug/SwiftKnexMigration create CreateUser
#
# Created /YourProject/Sources/Migration/20170116015823_CreateUser.swift
#
After create migration class file, edit it like following.
The created migration class has following methods.
Performed on migrate:latest
Performed on migrate:rollback
import SwiftKnex
import Foundation
class Migration_20170116015823_CreateUser: Migratable {
var name: String {
return "\(Mirror(reflecting: self).subjectType)"
}
func up(_ migrator: Migrator) throws {
let create = Create(
table: "users",
fields: [
Schema.integer("id").asPrimaryKey().asAutoIncrement(),
Schema.string("name").asIndex().asNotNullable(),
Schema.string("email").asUnique().asNotNullable()
])
.hasTimestamps()
.index(columns: ["name", "email"], unique: true)
try migrator.run(create)
}
func down(_ migrator: Migrator) throws {
try migrator.run(Drop(table: "users"))
}
}
Create main.swift
in the {$PROJ}/Sources/Migration
directory that is created by Migrate create
at previous section.
And copy/paste the following code into your {$PROJ}/Sources/Migration/main.swift
and then, replace the class names in the knexMigrations
array to correct names, and change the database configuration depending on your environment.
You need to add the new class name(s) to the knexMigrations
at every migration resource created.
main.swif example
import SwiftKnex
let knexMigrations: [Migratable] = [
Migration_20170116015823_CreateUser()
]
let config = KnexConfig(
host: "localhost",
user: "root",
database: "swift_knex_test"
)
try Migrator.run(config: config, arguments: CommandLine.arguments, knexMigrations: knexMigrations)
after editing main.swift, run swift build
swift build
After that, you only need to run the migration
Current supporting commands are
migrate:latest
: Perform to migrate recent unmigrated files.migrate:rollback
: Rollback the migrations recent performed.(The rollback unit is grouped bybatch
number)
.build/debug/Migration migrate:latest
.build/debug/Migration migrate:rollback
TODO
Go Style async query performing and syncronization with Prorsum
import Prorsum
let chan = Channel<ResultSet>.make()
go {
let rows = try! knex().table("users").where("id" == 1).fetch()
try chan.send(rows!)
}
go {
let rows = try! knex().table("users").where("id" == 2).fetch()
try chan.send(rows!)
}
print(try! chan.receive())
print(try! chan.receive())
The base Connection and Querying library that used in SwiftKnex.
TODO
Prorsum is released under the MIT license. See LICENSE for details.