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
21 changes: 16 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ jobs:
- postgres:16
- postgres:14
swift-image:
- swift:6.1-noble
- swift:6.2-noble
- swift:6.3-noble
- swift:6.4-noble
include:
- postgres-image: postgres:18
postgres-auth: scram-sha-256
Expand Down Expand Up @@ -82,7 +82,7 @@ jobs:
linux-integration:
if: ${{ !(github.event.pull_request.draft || false) }}
runs-on: ubuntu-latest
container: swift:6.3-noble
container: swift:6.4-noble
services:
*postgres_host_a:
image: postgres:18
Expand Down Expand Up @@ -118,10 +118,10 @@ jobs:
fail-fast: false
matrix:
include:
- macos-version: macos-15
xcode-version: latest-stable
- macos-version: macos-26
xcode-version: latest-stable
- macos-version: xcode-27
xcode-version: latest-stable
runs-on: ${{ matrix.macos-version }}
env:
POSTGRES_HOSTNAME: 127.0.0.1
Expand Down Expand Up @@ -150,20 +150,31 @@ jobs:

musl:
runs-on: ubuntu-latest
container: swift:6.3-noble
container: swift:6.4-noble
timeout-minutes: 30
steps:
- name: Check out code
uses: actions/checkout@v7
- name: Install linux dependencies
run: apt-get update && apt-get install -y curl jq
- name: Install SDK
shell: bash
run: |
version_num="$(swiftc --version | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' | head -1)"
if [[ -z "${version_num/???/}" ]]; then
version_num="${version_num}.0"
fi
tag="swift-${version_num}-RELEASE"
sdk_info="$(curl -s https://www.swift.org/api/v1/install/releases.json | jq -r --arg tag "$tag" '.[] | select(.tag == $tag).platforms[] | select(.platform == "static-sdk") | [.version, .checksum] | @tsv')"
version="$(echo "$sdk_info" | cut -f1)"
checksum="$(echo "$sdk_info" | cut -f2)"
swift sdk install "https://download.swift.org/$(echo $tag | tr [A-Z] [a-z])/static-sdk/$tag/${tag}_static-linux-${version}.artifactbundle.tar.gz" --checksum "$checksum"
- name: Build
run: swift build --swift-sdk x86_64-swift-linux-musl

submit-dependencies:
permissions:
contents: write
if: ${{ github.event_name == 'push' }}
uses: vapor/ci/.github/workflows/submit-deps.yml@main
Comment thread
gwynne marked this conversation as resolved.
Dismissed
secrets: inherit
2 changes: 2 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ let package = Package(
.package(url: "https://github.com/vapor/postgres-nio.git", from: "1.30.1"),
.package(url: "https://github.com/vapor/sql-kit.git", from: "3.34.0"),
.package(url: "https://github.com/vapor/async-kit.git", from: "1.21.0"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.14.0")
],
targets: [
.target(
Expand All @@ -32,6 +33,7 @@ let package = Package(
dependencies: [
.target(name: "PostgresKit"),
.product(name: "SQLKitBenchmark", package: "sql-kit"),
.product(name: "InMemoryLogging", package: "swift-log")
],
swiftSettings: swiftSettings
),
Expand Down
88 changes: 88 additions & 0 deletions Sources/PostgresKit/PostgresConnectionDatabase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import PostgresNIO
import SQLKit

extension PostgresConnection {
@inlinable
public func sql(queryLogLevel: Logger.Level? = .debug, logger: Logger? = nil) -> some SQLDatabase {
self.sql(encodingContext: .default, decodingContext: .default, queryLogLevel: queryLogLevel, logger: logger)
}

public func sql(
encodingContext: PostgresEncodingContext<some PostgresJSONEncoder>,
decodingContext: PostgresDecodingContext<some PostgresJSONDecoder>,
queryLogLevel: Logger.Level? = .debug,
logger: Logger? = nil,
) -> some SQLDatabase {
PostgresConnectionSQLDatabase(
connection: self,
encodingContext: encodingContext,
decodingContext: decodingContext,
queryLogLevel: queryLogLevel,
_logger: logger
)
}
}

private struct PostgresConnectionSQLDatabase<E: PostgresJSONEncoder, D: PostgresJSONDecoder> {
let connection: PostgresConnection
let encodingContext: PostgresEncodingContext<E>
let decodingContext: PostgresDecodingContext<D>
let queryLogLevel: Logger.Level?
let _logger: Logger?
}

extension PostgresConnectionSQLDatabase: SQLDatabase {
var logger: Logger {
self._logger ?? connection.logger
}

var eventLoop: any EventLoop {
connection.eventLoop
}

var dialect: any SQLDialect {
PostgresDialect()
}

func execute(sql query: any SQLExpression, _ onRow: @escaping @Sendable (any SQLRow) -> ()) -> NIOCore.EventLoopFuture<Void> {
let (sql, binds) = self.serialize(query)

if let queryLogLevel = self.queryLogLevel {
self.logger.log(level: queryLogLevel, "Executing query", metadata: ["sql": .string(sql), "binds": .array(binds.map { .string("\($0)") })])
}
return self.eventLoop.makeCompletedFuture {
try makeBindings(from: binds)
}.flatMap { bindings in
connection.query(.init(unsafeSQL: sql, binds: bindings), logger: self.logger) {
onRow($0.sql(decodingContext: self.decodingContext))
}
}.map { _ in }
}

func execute(sql query: any SQLExpression, _ onRow: @escaping @Sendable (any SQLRow) -> ()) async throws {
let (sql, binds) = self.serialize(query)

if let queryLogLevel = self.queryLogLevel {
self.logger.log(level: queryLogLevel, "Executing query", metadata: ["sql": .string(sql), "binds": .array(binds.map { .string("\($0)") })])
}

let bindings = try makeBindings(from: binds)

let result = try await connection.query(.init(unsafeSQL: sql, binds: bindings), logger: logger)
for try await row in result {
onRow(row.sql(decodingContext: decodingContext))
}
}

func makeBindings(from binds: [any Encodable & Sendable]) throws -> PostgresBindings {
var bindings = PostgresBindings(capacity: binds.count)
for bind in binds {
try PostgresDataTranslation.encode(value: bind, in: self.encodingContext, to: &bindings)
}
return bindings
}

func withSession<R>(_ closure: @escaping @Sendable (any SQLDatabase) async throws -> R) async throws -> R {
try await closure(self)
}
}
Loading
Loading