Open-Source Wikis

/

Bun

/

Systems

/

SQL clients

oven-sh/bun

SQL clients

src/sql/ implements native Postgres and MySQL clients exposed to JavaScript as Bun.sql. SQLite is implemented separately in src/bun.js/bindings/sqlite/ and exposed as bun:sqlite.

Files

src/sql/
├── postgres/        # Postgres protocol (~lots of files)
├── postgres.zig     # entry struct
├── mysql/           # MySQL protocol
├── mysql.zig        # entry struct
└── shared/          # row decoders, types, prepared statements, connection pool

The user-facing class declaration is src/bun.js/api/sql.classes.ts. The implementation in Zig wraps the protocol parser and exposes an async query, transaction, tagged-template API.

Postgres client

The Postgres client speaks the wire protocol directly — no libpq dependency. It supports:

  • Cleartext, MD5, SCRAM-SHA-256 authentication.
  • TLS over uSockets/BoringSSL.
  • Prepared statements with caching keyed by the SQL text.
  • The COPY protocol for bulk import/export (in progress).
  • Pipelined queries — multiple statements may be in flight on one connection.
  • Notification listeners (LISTEN/NOTIFY).
  • Per-connection statement timeout, pool size, idle timeout.

The wire format is implemented as a state machine in src/sql/postgres/. Row decoding uses the per-column type OID to dispatch into a typed decoder (text or binary). Binary decoders cover numerics, JSON, arrays, dates, intervals, and geometric types.

MySQL client

The MySQL client targets MySQL 5.7+ and MariaDB 10+. It supports caching_sha2_password and mysql_native_password, prepared statements, and TLS. Wire format and authentication state machines are in src/sql/mysql/.

Shared layer

src/sql/shared/ holds the connection pool, the statement cache, the row decoder dispatch, and the type system shared by both clients. The pool is a max-size pool with idle eviction; checkouts wait on a ConcurrentTask.

JavaScript surface

import { sql } from 'bun';

const db = sql('postgres://user@localhost/db');
const rows = await db`SELECT * FROM users WHERE id = ${id}`;
await db.begin(async (tx) => {
  await tx`INSERT INTO logs ${{ msg }}`;
});

The tagged template returns a thenable that runs the query when awaited. Parameter substitution uses $1, $2, ... in Postgres and ? in MySQL — the template compiler picks the right form per dialect.

Performance

  • Prepared statement cache means repeated queries skip the parse/plan phase.
  • Result rows are decoded directly into JS values without an intermediate buffer.
  • COPY FROM STDIN / COPY TO STDOUT (Postgres) is plumbed as a ReadableStream.

Integration points

  • Event loop — Connection events post tasks; query results resolve promises.
  • HTTP stack — Doesn't share with HTTP, but uses uSockets and BoringSSL.
  • JSC bindingssrc/bun.js/api/sql.classes.ts defines the JS shape.

Entry points for modification

  • To add a Postgres type decoder, edit src/sql/postgres/ (look for the OID dispatch table).
  • To add an authentication method, edit the auth state machine in the corresponding dialect directory.
  • To change connection pooling, edit src/sql/shared/.

Key source files

File Purpose
src/sql/postgres/ Postgres protocol.
src/sql/mysql/ MySQL protocol.
src/sql/shared/ Pool, cache, decoders.
src/bun.js/api/sql.classes.ts JS-visible class declaration.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

SQL clients – Bun wiki | Factory