Bun.sql + Bun.redis: remove unsafe from sql_jsc and valkey_jsc

Merged
JA
1mo ago
+4386-5546

What

Same programme as #40055 … #40262. Stacked on #40139 (base branch claude/socket-zero-unsafe) and includes #40187's timer branch by merge (needs TimerRef); retarget to main once those land.

src/sql_jsc/: PostgresSQLConnection.rs 21 → 0, MySQLConnection.rs 14 → 0, PostgresSQLQuery.rs 10 → 0, MySQLRequestQueue.rs 9 → 0, JSMySQLConnection.rs 6 → 0, CachedStructure.rs 4 → 0, MySQLQuery.rs/MySQLValue.rs/JSMySQLQuery.rs/SASL.rs/ConnectionCtorArgs.rs/*Context.rs → 0, jsc.rs 35 → 1, SQLDataCell.rs 11 → 1, DataCell.rs 2 → 1 (residuals are all-safe extern blocks). src/runtime/valkey_jsc/: js_valkey.rs 33 → 0, valkey.rs 5 → 0, js_valkey_functions.rs 2 → 0. Collateral: dispatch.rs 99 → 88, hw_exports.rs 65 → 41 (ValkeyDeferredClose tag removed — it's a ManagedTask now).

  • Connections are CellRefCounted with typed slots for the socket/timer/queued-task holders; NsSocketEvents/SocketHandler::on_* take ThisPtr<Owner>; request queues are VecDeque<RefPtr<Query>>; SQL timers arm through timer_owner_this!; SqlRuntimeHooks is {with_sql_state, timer_insert(TimerRef), timer_remove(TimerRef), ssl_config_from_js} (all safe; the Blob hook is gone — sql_jsc uses bun_jsc::webcore::Blob directly).
  • New primitives: bun_jsc::auto_flusher::{AutoFlusher<T>, AutoFlushTarget} (holds a ref across the callback), bun_jsc::url::OwnedUrl, JSObject::create_structure_for, JSValue::borrow_bytes_for_off_thread -> BorrowedBytes<'_>, From<RefPtr<T>> for ScopedRef<T>, JsCell::get_mut_unique(&mut self), ManagedTask::{new_boxed, RunOnce} (same as #40204's), boringssl_sys::{pbkdf2_hmac_sha256, err_error_string_n}, bun_sha_hmac SHA*::digest, uws_sys::AnySocket::ssl_mut.

Noted, unchanged shapes: SQLDataCell still carries raw &[u8] addresses as POD for raw/bytea (C++ consumes it synchronously); MySQL Reader/Writer back-refs alias the connection exactly as before.

Pre-existing bugs fixed in passing: CachedStructure::build_from_columns leaked one atom-string ref per named column per structure; Postgres do_run kept a raw pointer into the statements map across user-JS-running bind conversion (now re-looked-up by name); a re-entrant named-statement slot fill leaked; MySQL on_close would over-release if it fired without a TCP on_open. Left as-is and flagged: clean_up_requests' Pending-without-statement arm continues without discarding.

Testing

Debug+ASAN against Docker Postgres / MySQL / Redis: all 56 test/js/sql/*.test.ts pass except should not timeout in long results (1 pg + 3 mysql) which fails identically on the parent commit's debug build (10 s budget); test/js/valkey/** 1305 pass (valkey-gc.test.ts's 3 LSAN cases identical on baseline — unsymbolized bun:ffi dlopen); third-party pg/pg-gateway/postgres pass. Hand-driven: 500 queued queries + close + GC, close mid-query, 20 pool open/close cycles (pg + mysql; wrapper counts return to baseline), a Worker with pg/mysql/redis connections and in-flight queries terminated × 6 — no ASAN output. clippy clean on all touched crates; rust-check-all windows-msvc + apple-darwin pass.