Embedded Swift support for SQLKit - #4
Draft
scottmarchant wants to merge 1 commit into
Draft
Conversation
scottmarchant
force-pushed
the
feat/embedded-support
branch
from
July 29, 2026 00:20
1828522 to
639bc6a
Compare
scottmarchant
force-pushed
the
feat/wasi-nio-free
branch
from
July 29, 2026 00:20
224a265 to
84ba06c
Compare
This was referenced Jul 29, 2026
Motivation:
Embedded Swift has no Codable, no reflection, no existential metatypes, and no
`_StringProcessing`. A number of SQLKit declarations depend on one of those and
are the only thing standing between the SwiftNIO-free configuration and a
`wasm32-unknown-wasip1-embedded` build.
The bound-parameter constraint is the interesting case. SQLKit spells it
`Encodable`, but SQLKit itself never encodes a bound value: `SQLBind` hands the
existential to `SQLSerializer.write(bind:)`, which appends it to `binds` and emits
a placeholder. The constraint is a marker, and the actual extraction happens in
the driver.
That makes a vacuous `#if hasFeature(Embedded) public protocol Encodable {}` shim
look attractive — it would leave every use site unchanged. It does not work. The
marker exists precisely to carry a value across the module boundary to a driver
that must get the value back out, and a protocol with no requirements carries
nothing: under Embedded there is no `encode(to:)` to call, no reflection, and no
dynamic cast to fall back on, so the bind path would compile and then be inert.
Shadowing `Swift.Encodable` in a public signature would also collide with the
same shim in other modules and cannot be made `internal`, because the constraint
appears in public and `@inlinable` declarations.
Modifications:
Introduce `SQLBindable`, the constraint used for bound parameter values. It is
`typealias SQLBindable = Encodable` except under Embedded Swift, where it resolves
to a new lightweight `SQLBindValue` protocol carrying a driver-neutral
`SQLDataValue`, with conformances for the standard primitives. `SQLBindValue` is
what replaces `encode(to:)` for the driver.
The public declarations that spelled `some`/`any Encodable & Sendable` for a bound
value now spell `some`/`any SQLBindable & Sendable`. Off Embedded that is the same
type, so this is source- and ABI-compatible. Declarations that take a Codable
*model* rather than a bound value keep saying `Encodable`; they live inside
`!hasFeature(Embedded)` regions, where the two spellings are identical anyway.
Everything else is elision:
- Gate the Codable engine (`SQLQueryEncoder`, `SQLRowDecoder`, `SomeCodingKey`,
the `SQLCodingUtilities` helpers, and the model-shaped builder overloads
`set(model:)` / `insert(models:)` / `decode(model:)`) behind
`#if !hasFeature(Embedded)`.
- Move the generic `withSession(_:)` protocol requirement to an extension default
under Embedded: a generic method cannot go in a witness table there, which would
otherwise make `any SQLDatabase` unusable.
- Replace reflection- and existential-dependent paths (metatype casts in
`SQLDatabaseReportedVersion` and the deprecated shims, description formatting in
`SQLSerializer`/`SQLQueryString`, the `as?`-based `LIMIT 1` optimization in
`first()`) with Embedded-safe equivalents.
- `StringHandling`: `trimmingPrefix(_:)` and the regex-backed replacement helper
come from `_StringProcessing`, which the Embedded stdlib does not ship; fall
through to the existing hand-rolled implementations there.
- `SQLKitBenchmark` needs XCTest and Codable, so the whole target's contents are
gated on `!hasFeature(Embedded)`.
Every `#if` encloses the doc comment of the declaration it gates rather than
sitting between the two, which would detach the comment and silently empty the
published documentation for that symbol.
Result:
SQLKit compiles for `wasm32-unknown-wasip1-embedded`.
On every other target the only change to the generated symbol graph is the added
`SQLBindable` typealias: every other symbol is still present, with byte-identical
`docComment` line counts, and `diagnose-api-breaking-changes` reports no
differences.
Building for the Embedded target additionally requires an Embedded-clean
`apple/swift-log`; see the pull request description.
scottmarchant
force-pushed
the
feat/wasi-nio-free
branch
from
July 29, 2026 03:24
84ba06c to
f008acf
Compare
scottmarchant
force-pushed
the
feat/embedded-support
branch
from
July 29, 2026 03:24
639bc6a to
1ecea99
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Makes SQLKit compile under Embedded Swift. This builds on #3 (
feat/wasi-nio-free) and retargets tobase/vapor-mainonce that merges, so this diff shows only the Embedded work. It is research grade and lower priority than #3, which does not depend on it; if this direction isn't wanted, dropping it costs the WASI support nothing.The part that needs real discussion is the bound-parameter type. Embedded Swift has no Codable, and SQLKit's bound-parameter constraint is spelled
Encodableacross roughly eighty public declarations, so the constraint becomes a typealias that resolves differently there:Off Embedded,
SQLBindableisEncodable, so everysome/any Encodable & Sendablethat now readssome/any SQLBindable & Sendableis the same type as before.swift package diagnose-api-breaking-changesreports no breaking changes in SQLKit (the same check the requiredunit-tests / api-breakagejob runs), and the 169-test suite is untouched and green. What remains is that it reads like a large mechanical diff across thirty files, and I know that's a lot to ask of a reviewer; the alternative, duplicating every one of those declarations under a gate, seemed worse. Happy to be told no.The rest of the Embedded work:
SQLQueryEncoder,SQLRowDecoder,SomeCodingKey, theSQLCodingUtilitieshelpers, and the model-shaped builder overloads) gates out under Embedded Swift.withSession(_:)moves from protocol requirement to extension default there, since a generic requirement can't be placed in a witness table under Embedded Swift without makingany SQLDatabaseunusable.SQLDatabaseReportedVersioncomparisons, description formatting) get Embedded-safe equivalents, implicit array upcasts are boxed explicitly, and one key-path literal becomes a closure.StringHandling'strimmingPrefix(_:)and regex-based helpers fall through to the existing hand-rolled implementations, since the Embedded stdlib ships neither.SQLKitBenchmark's contents gate on!hasFeature(Embedded)(it needs XCTest and Codable), not on any platform; regular WASI still compiles the benchmark target, verified.Same prerequisite as the sqlite-nio Embedded PR: an Embedded-clean swift-log. Upstream 1.14.0 does not compile under Embedded Swift and the build dies inside
Loggingfirst. Verified against a locally patched clone; the patch is not committed here and the manifest still points at upstreamapple/swift-log. swift-collections needed nothing.Verified locally: native build and 169 tests green, no API breakage against the base, regular WASI green, and the Embedded build green (
DEVELOPMENT-SNAPSHOT-2026-06-12-a-wasm32-unknown-wasip1-embeddedwith the patched swift-log).