Skip to content

feat(postgres, core): PgConnection::connect_with_socket and a wasm-safe Instant - #4444

Closed
iamralch wants to merge 2 commits into
transact-rs:mainfrom
iamralch:wasm-custom-socket
Closed

iamralch wants to merge 2 commits into
transact-rs:mainfrom
iamralch:wasm-custom-socket

Conversation

@iamralch

@iamralch iamralch commented Oct 4, 2026

Copy link
Copy Markdown

Does your PR solve an issue?

fixes #4426

This is option 1 from the discussion there: socket-only, Postgres only. A caller-supplied TLS upgrade, which a MySQL connect_with_socket would need, is left for a follow-up if this direction is accepted.

Two commits, each usable on its own. Happy to split them into separate PRs if that's easier to review.

  1. fix(core): use a wasm-safe Instant on wasm32-unknown-unknown. QueryLogger::new calls std::time::Instant::now() for every query, and that panics on wasm32-unknown-unknown with time not implemented on this platform. This adds sqlx_core::rt::Instant, which is std::time::Instant everywhere else and web_time::Instant (the host's performance.now()) on that target. It's used in the query logger and the Postgres, MySQL and SQLite migrators. web-time is a dependency on that target only.
  2. feat(postgres): add PgConnection::connect_with_socket. This takes any sqlx_core::net::Socket the caller has already connected. The host, port and socket path in the options are ignored. Everything else, including ssl_mode, applies as it does for connect_with. establish now goes through the same path after opening its own socket, so existing connections are unchanged.

The motivating host is Cloudflare Workers, which hands out its own TCP sockets (e.g. to reach Postgres through Hyperdrive) and has no tokio. TLS doesn't need a hook for Postgres: a caller whose host upgrades the socket itself can send the SSLRequest, upgrade, and pass the result in with ssl_mode = Disable.

Tests

  • New it_connects_with_socket in tests/postgres/postgres.rs: a tokio TcpStream opened by the test, then connect_with_socket.
  • Existing tests/postgres/postgres.rs (58 passed, 2 ignored) and tests/postgres/migrate.rs (4 passed).
  • On wasm32-unknown-unknown, a Worker under wrangler dev --local against Postgres 17 with SCRAM auth: connect, bound parameters and scalar types, 2,000 rows over many socket reads, unique-violation mapping, rollback and commit, prepared-statement reuse, sqlx::migrate!(), ping and close. Reverting only the Instant commit makes the same Worker panic.

A downstream crate built on this branch, with those scenarios in CI: sqlx-contrib/sqlx-cloudflare#16 (sqlx-cloudflare-hd).

Is this a breaking change?

No. Both changes only add public items (PgConnection::connect_with_socket, sqlx_core::rt::Instant). sqlx_core::rt::Instant is std::time::Instant on every target except wasm32-unknown-unknown, where Instant::now() panicked before. Existing connect_with behaviour is unchanged; it now delegates to the same code path after opening its socket.

`std::time::Instant::now()` panics on `wasm32-unknown-unknown` ("time not
implemented on this platform"), and `QueryLogger::new` calls it for every
query, so no driver can run a query there, even over a transport the host
provides.

Add `sqlx_core::rt::Instant`: `std::time::Instant` everywhere else, and
`web_time::Instant`, which reads the host's `performance.now()`, on that
target. Use it in the query logger and the Postgres, MySQL and SQLite
migrators. `web-time` is only a dependency on that target; nothing changes
elsewhere.
sqlx opens its own sockets through `sqlx_core::net::connect_tcp`, which needs
a supported async runtime; without one it panics in `missing_rt`. Hosts that
hand out their own socket type, such as Cloudflare Workers
(`wasm32-unknown-unknown`, with `connect()` sockets), have no way to connect.

`connect_with_socket` takes any `sqlx_core::net::Socket` the caller has already
connected. The host, port and socket path in the options are ignored;
everything else, including `ssl_mode`, applies as it does for `connect_with`.
`establish` now goes through the same path after opening its own socket.
@abonander

Copy link
Copy Markdown
Collaborator

I don't want to support wasm32-unknown-unknown. It is too ridiculous of a target to try to support, and I don't want anything in our manifests or CI contig that implies that it's supported. I'm continuously frustrated by the fact that WASM problems become my problems even when I never asked for them to be.

I recommend trying CloudFlare's experimental support for WASI, which might work without other code changes. As for accepting external sockets, there's already several other open PRs that I have to review.

@abonander abonander closed this Oct 14, 2026
Themezv added a commit to MemeBattle/monorepo that referenced this pull request Oct 18, 2026
…e-hd (#794)

* build: pin sqlx to the MemeBattle fork (connect_with_socket, wasm Instant)

sqlx 0.9.0 cannot open a Postgres connection on wasm32-unknown-unknown:
PgStream::connect only knows TCP and unix sockets, and
std::time::Instant::now() traps there. MemeBattle/sqlx tag
v0.9.0-memebattle.1 is v0.9.0 plus the two commits upstream refused in
transact-rs/sqlx#4444 (PgConnection::connect_with_socket and a wasm-safe
Instant). The only lockfile changes are the sqlx crates' source and
web-time, which the fork's sqlx-core uses on wasm32.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* build: vendor sqlx-cloudflare-hd from sqlx-contrib/sqlx-cloudflare#16

Copied from branch feat/sqlx-cloudflare-hd at 9499455d (unpublished, MIT).
It opens a Worker socket to a Hyperdrive binding and hands it to sqlx's own
Postgres driver through PgConnection::connect_with_socket. Sources, README
and LICENSE are verbatim; only Cargo.toml is rewritten to stand alone outside
the upstream workspace. VENDOR.md records the source and every change.

worker 0.8.6+ needs wasm-bindgen 0.2.128+, so the lockfile moves the
wasm-bindgen family (wasm-bindgen 0.2.129, js-sys/web-sys 0.3.106,
wasm-bindgen-futures 0.4.79); those only build on wasm targets. oxfmt now
skips vendor/ so the copied Markdown stays byte for byte.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* ci(cas): run the CAS workflow on vendor changes

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* ci(cas): compile the vendored Hyperdrive adapter for wasm32

Adding vendor/** to the path filter only decided when the workflow runs;
every job still built apps/cas alone, and CAS does not depend on
sqlx-cloudflare-hd, so a regression in the adapter or in the wasm-only code
of the sqlx fork could merge with every check green. A dedicated job now
checks and lints the crate with its postgres feature for
wasm32-unknown-unknown, the target it exists for.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Postgres: connect over a caller-provided socket, and a wasm-safe Instant, for hosts like Cloudflare Workers

2 participants