Skip to content
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
23 changes: 2 additions & 21 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,5 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Generate certificates
run: make test-cert
- name: Install MySQL client
run: sudo apt-get update && sudo apt-get install -y mariadb-client
- name: Start MariaDB Docker Container
run: docker compose up -d --build
- name: Wait For MariaDB Docker Container
run: |
for i in {1..30}; do
if docker compose exec -T mariadb mariadb-admin ping -u mariadb -pmariadb --silent; then
exit 0
fi
sleep 1
done
docker compose logs mariadb
exit 1
- name: Run Tests
run: swift test --parallel --enable-code-coverage
- name: Stop MariaDB Docker Container
if: always()
run: docker compose down -v
- name: Run Docker Tests
run: make docker-test
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ fix-headers:
curl -s $(baseUrl)/check-swift-headers.sh | bash -s -- --fix


test-cert:
test-certs:
rm -rf docker/mariadb/certificates && mkdir -p docker/mariadb/certificates && cd docker/mariadb/certificates && ../scripts/generate-certificates.sh

test:
swift test --parallel

docker-test:
docker build -t feather-database-mysql-tests . -f ./docker/tests/Dockerfile && docker run --rm feather-database-mysql-tests
make test-certs && docker compose up --build --abort-on-container-exit --exit-code-from test test
6 changes: 3 additions & 3 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 18 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,38 @@ let package = Package(
.visionOS(.v2),
],
products: [
.library(name: "MySQLNIOExtras", targets: ["MySQLNIOExtras"]),
.library(name: "FeatherDatabaseMySQL", targets: ["FeatherDatabaseMySQL"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-log", from: "1.6.0"),
.package(url: "https://github.com/vapor/mysql-nio", from: "1.8.0"),
.package(url: "https://github.com/feather-framework/feather-database", exact: "1.0.0-beta.5"),
.package(url: "https://github.com/feather-framework/feather-database", exact: "1.0.0-rc.1"),
// [docc-plugin-placeholder]
],
targets: [
.target(
name: "MySQLNIOExtras",
dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "MySQLNIO", package: "mysql-nio"),
],
swiftSettings: defaultSwiftSettings
),
.target(
name: "FeatherDatabaseMySQL",
dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "MySQLNIO", package: "mysql-nio"),
.product(name: "FeatherDatabase", package: "feather-database"),
.target(name: "MySQLNIOExtras"),
],
swiftSettings: defaultSwiftSettings
),
.testTarget(
name: "MySQLNIOExtrasTests",
dependencies: [
.target(name: "MySQLNIOExtras"),
],
swiftSettings: defaultSwiftSettings
),
Expand Down
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
MySQL/MariaDB driver implementation for the abstract [Feather Database](https://github.com/feather-framework/feather-database) Swift API package.

[
![Release: 1.0.0-beta.5](https://img.shields.io/badge/Release-1%2E0%2E0--beta%2E5-F05138)
![Release: 1.0.0-rc.1](https://img.shields.io/badge/Release-1%2E0%2E0--rc%2E1-F05138)
](
https://github.com/feather-framework/feather-database-mysql/releases/tag/1.0.0-beta.5
https://github.com/feather-framework/feather-database-mysql/releases/tag/1.0.0-rc.1
)

## Features
Expand Down Expand Up @@ -36,7 +36,7 @@ MySQL/MariaDB driver implementation for the abstract [Feather Database](https://
Add the dependency to your `Package.swift`:

```swift
.package(url: "https://github.com/feather-framework/feather-database-mysql", exact: "1.0.0-beta.5"),
.package(url: "https://github.com/feather-framework/feather-database-mysql", exact: "1.0.0-rc.1"),
```

Then add `FeatherDatabaseMySQL` to your target dependencies:
Expand Down Expand Up @@ -122,9 +122,6 @@ catch {
}
```

> [!WARNING]
> This repository is a work in progress, things can break until it reaches v1.0.0.

## Other database drivers

The following database client implementations are also available for use:
Expand All @@ -144,4 +141,3 @@ The following database client implementations are also available for use:
## Contributing

[Pull requests](https://github.com/feather-framework/feather-database-mysql/pulls) are welcome. Please keep changes focused and include tests for new logic. 🙏

107 changes: 91 additions & 16 deletions Sources/FeatherDatabaseMySQL/DatabaseClientMySQL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
// DatabaseClientMySQL.swift
// feather-database-mysql
//
// Created by Tibor Bödecs on 2026. 01. 10..
// Created by Tibor Bödecs on 2026. 01. 10.
//

import FeatherDatabase
import Logging
import MySQLNIO
import MySQLNIOExtras

/// A MySQL-backed database client.
///
Expand All @@ -16,8 +17,13 @@ public struct DatabaseClientMySQL: DatabaseClient {

public typealias Connection = DatabaseConnectionMySQL

var connection: DatabaseConnectionMySQL
var logger: Logger
private enum Storage {
case connection(DatabaseConnectionMySQL)
case client(MySQLClient)
}

private let storage: Storage
private let logger: Logger

/// Create a MySQL database client.
///
Expand All @@ -29,16 +35,31 @@ public struct DatabaseClientMySQL: DatabaseClient {
connection: MySQLConnection,
logger: Logger
) {
self.connection = .init(
connection: connection,
logger: logger
self.storage = .connection(
.init(
connection: connection,
logger: logger
)
)
self.logger = logger
}

/// Create a MySQL database client backed by a connection pool.
///
/// - Parameters:
/// - client: The pooled MySQL client to use.
/// - logger: The logger for database operations.
public init(
client: MySQLClient,
logger: Logger
) {
self.storage = .client(client)
self.logger = logger
}

// MARK: - database api

/// Execute work using the stored connection.
/// Execute work using the stored connection or pooled client.
///
/// The closure is executed with the current connection.
/// - Parameter closure: A closure that receives the MySQL connection.
Expand All @@ -48,14 +69,27 @@ public struct DatabaseClientMySQL: DatabaseClient {
public func withConnection<T>(
_ closure: (Connection) async throws -> T
) async throws(DatabaseError) -> T {
do {
return try await closure(connection)
}
catch let error as DatabaseError {
throw error
}
catch {
throw .connection(error)
switch storage {
case .connection(let connection):
return try await withConnection(connection, closure)
case .client(let client):
do {
return try await client.withConnection { connection in
try await withConnection(
DatabaseConnectionMySQL(
connection: connection,
logger: logger
),
closure
)
}
}
catch let error as DatabaseError {
throw error
}
catch {
throw .connection(error)
}
}
}

Expand All @@ -69,7 +103,49 @@ public struct DatabaseClientMySQL: DatabaseClient {
public func withTransaction<T>(
_ closure: (Connection) async throws -> T
) async throws(DatabaseError) -> T {
switch storage {
case .connection(let connection):
return try await withTransaction(connection, closure)
case .client(let client):
do {
return try await client.withConnection { connection in
try await withTransaction(
DatabaseConnectionMySQL(
connection: connection,
logger: logger
),
closure
)
}
}
catch let error as DatabaseError {
throw error
}
catch {
throw .connection(error)
}
}
}

private func withConnection<T>(
_ connection: DatabaseConnectionMySQL,
_ closure: (Connection) async throws -> T
) async throws(DatabaseError) -> T {
do {
return try await closure(connection)
}
catch let error as DatabaseError {
throw error
}
catch {
throw .connection(error)
}
}

private func withTransaction<T>(
_ connection: DatabaseConnectionMySQL,
_ closure: (Connection) async throws -> T
) async throws(DatabaseError) -> T {
do {
try await connection.run(query: "START TRANSACTION;") { _ in }
}
Expand Down Expand Up @@ -118,5 +194,4 @@ public struct DatabaseClientMySQL: DatabaseClient {
throw DatabaseError.transaction(txError)
}
}

}
Loading
Loading