A high-performance, non-blocking database driver for PostgreSQL, MySQL, and SQLite, written for Kotlin Native.
Important
The project is in a very early stage; thus, breaking changes should be expected.
đź“– Documentation
🏠Homepage (under construction)
Currently, the driver supports:
PostgreSQL
MySQL
SQLite
implementation("io.github.smyrgeorge:sqlx4k-postgres:x.y.z")
// or for MySQL
implementation("io.github.smyrgeorge:sqlx4k-mysql:x.y.z")
// or for SQLite
implementation("io.github.smyrgeorge:sqlx4k-sqlite:x.y.z")
We support the following targets:
- iosArm64
- androidNativeX64
- androidNativeArm64
- macosArm64
- macosX64
- linuxArm64
- linuxX64
- mingwX64
- wasmJs (potential future candidate)
- jvm (potential future candidate)
The driver fully supports non-blocking io.
You can set the maxConnections
from the driver constructor:
// Additionally, you can set minConnections, acquireTimeout, idleTimeout, etc.
val options = Driver.Pool.Options.builder()
.maxConnections(10)
.build()
val db = PostgreSQL(
host = "localhost",
port = 15432,
username = "postgres",
password = "postgres",
database = "test",
options = options
)
val db = MySQL(
host = "localhost",
port = 13306,
username = "mysql",
password = "mysql",
database = "test",
options = options
)
val db = SQLite(
database = "test.db",
options = options
)
// With named parameters:
val st1 = Statement
.create("select * from sqlx4k where id = :id")
.bind("id", 65)
db.fetchAll(st1).getOrThrow().map {
val id: ResultSet.Row.Column = it.get("id")
Test(id = id.asInt())
}
// With positional parameters:
val st2 = Statement
.create("select * from sqlx4k where id = ?")
.bind(0, 65)
db.fetchAll(st2).getOrThrow().map {
val id: ResultSet.Row.Column = it.get("id")
Test(id = id.asInt())
}
val tx1: Transaction = db.begin().getOrThrow()
tx1.execute("delete from sqlx4k;").getOrThrow()
val res: ResultSet = tx1.fetchAll("select * from sqlx4k;").getOrThrow().forEach {
println(debug())
}
tx1.commit().getOrThrow()
For this operation you will need to include the KSP
plugin to your project.
plugins {
alias(libs.plugins.ksp)
}
// Then you need to configure the processor (will generate the necessary code files).
ksp {
arg("output-package", "io.github.smyrgeorge.sqlx4k.examples.postgres")
arg("output-filename", "GeneratedQueries")
}
dependencies {
ksp(implementation("io.github.smyrgeorge:sqlx4k-codegen:x.y.z")) // Will generate code for all available targets.
}
Then create your data class that will be mapped to a table:
@Table("sqlx4k")
data class Sqlx4k(
@Id(insert = true) // Will be included in the insert query.
val id: Int,
val test: String
)
We also need to create the function definitions for the generated code:
// Filename: GeneratedQueries (same as `output-filename`).
// Also the package should be the same as `output-package`.
package io.github.smyrgeorge.sqlx4k.examples.postgres
import io.github.smyrgeorge.sqlx4k.Statement
// We only need to declare the functions,
// the actual code will be auto-generated.
expect fun Sqlx4k.insert(): Statement
expect fun Sqlx4k.update(): Statement
expect fun Sqlx4k.delete(): Statement
Then in your code you can use it like:
val insert: Statement = Sqlx4k(id = 66, test = "test").insert()
val affected = db.execute(insert).getOrThrow()
println("AFFECTED: $affected")
For more details take a look at the postgres
example.
db.listen("chan0") { notification: Postgres.Notification ->
println(notification)
}
(1..10).forEach {
db.notify("chan0", "Hello $it")
delay(1000)
}
Check here: https://github.com/smyrgeorge/sqlx4k-sqldelight
- PostgreSQL
- MySQL
- SQLite
- Transactions
- Listen/Notify Postgres
- INSERT/UPDATE/DELETE APIs (with code generation)
- Value encoders/decoders for basic data-types (in progress)
- Transaction isolation level
- Performance testing
- Testing
You will need the Rust
toolchain to build this project.
Check here: https://rustup.rs/
Note
By default the project will build only for your system architecture-os (e.g. macosArm64
, linuxArm64
, etc.)
Also, make sure that you have installed all the necessary targets (only if you want to build for all targets):
rustup target add aarch64-apple-ios
rustup target add x86_64-linux-android
rustup target add aarch64-linux-android
rustup target add aarch64-apple-darwin
rustup target add x86_64-apple-darwin
rustup target add aarch64-unknown-linux-gnu
rustup target add x86_64-unknown-linux-gnu
rustup target add x86_64-pc-windows-gnu
We also need to install cross
(tool that helps with cross-compiling)
cargo install cross --git https://github.com/cross-rs/cross
Then, run the build.
# will build only for macosArm64 target
./gradlew build
You can also build for specific targets.
./gradlew build -Ptargets=macosArm64,macosX64
To build for all available target run:
./gradlew build -Ptargets=all
./gradlew publishAllPublicationsToMavenCentralRepository -Ptargets=all
First you need to run start-up the postgres instance.
docker compose up -d
Then run the main
method.
./sqlx4k-postgres-examples/build/bin/macosArm64/releaseExecutable/sqlx4k-postgres-examples.kexe
See Main.kt
file for more examples (examples modules).
// Initialize the connection pool.
val db = PostgreSQL(
host = "localhost",
port = 15432,
username = "postgres",
password = "postgres",
database = "test",
maxConnections = 10
)
db.execute("drop table if exists sqlx4k;").getOrThrow()
// Make a simple query.
data class Test(val id: Int)
// You can also use RowMappers(s) to map your objects.
object TestRowMapper : RowMapper<Test> {
override fun map(rs: ResultSet, row: ResultSet.Row): Test {
val id: ResultSet.Row.Column = row.get("id")
return Test(id = id.asInt())
}
}
val res: List<Test> = db.fetchAll("select * from sqlx4k;", TestRowMapper).getOrThrow()
println(test)
Check for memory leaks with the leaks
tool.
First sign you binary:
codesign -s - -v -f --entitlements =(echo -n '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd"\>
<plist version="1.0">
<dict>
<key>com.apple.security.get-task-allow</key>
<true/>
</dict>
</plist>') ./examples/postgres/build/bin/macosArm64/releaseExecutable/postgres.kexe
Then run the tool:
leaks -atExit -- ./examples/postgres/build/bin/macosArm64/releaseExecutable/postgres.kexe
Sample output:
Process: postgres.kexe [54426]
Path: /Users/USER/*/postgres.kexe
Load Address: 0x1027ec000
Identifier: postgres.kexe
Version: 0
Code Type: ARM64
Platform: macOS
Parent Process: leaks [54424]
Date/Time: 2024-10-14 19:17:58.968 +0200
Launch Time: 2024-10-14 19:17:21.968 +0200
OS Version: macOS 15.0 (24A335)
Report Version: 7
Analysis Tool: /Applications/Xcode.app/Contents/Developer/usr/bin/leaks
Analysis Tool Version: Xcode 16.0 (16A242d)
Physical footprint: 37.1M
Physical footprint (peak): 38.5M
Idle exit: untracked
----
leaks Report Version: 4.0, multi-line stacks
Process 54426: 1847 nodes malloced for 656 KB
Process 54426: 0 leaks for 0 total leaked bytes.