Bun.sql + Bun.redis: remove unsafe from sql_jsc and valkey_jsc
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
CellRefCountedwith typed slots for the socket/timer/queued-task holders;NsSocketEvents/SocketHandler::on_*takeThisPtr<Owner>; request queues areVecDeque<RefPtr<Query>>; SQL timers arm throughtimer_owner_this!;SqlRuntimeHooksis{with_sql_state, timer_insert(TimerRef), timer_remove(TimerRef), ssl_config_from_js}(all safe; the Blob hook is gone — sql_jsc usesbun_jsc::webcore::Blobdirectly). - 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_hmacSHA*::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.