import type * as BunSQLite from "bun:sqlite"; declare module "bun" { /** * A connection reserved from the pool with {@link SQL.reserve}. Call * {@link release} to return it to the pool. */ interface ReservedSQL extends SQL, Disposable { /** * Releases the client back to the connection pool */ release(): void; } type ArrayType = | "BOOLEAN" | "BYTEA" | "CHAR" | "NAME" | "TEXT" | "CHAR" | "VARCHAR" | "SMALLINT" | "INT2VECTOR" | "INTEGER" | "INT" | "BIGINT" | "REAL" | "DOUBLE PRECISION" | "NUMERIC" | "MONEY" | "OID" | "TID" | "XID" | "CID" | "JSON" | "JSONB" | "JSONPATH" | "XML" | "POINT" | "LSEG" | "PATH" | "BOX" | "POLYGON" | "LINE" | "CIRCLE" | "CIDR" | "MACADDR" | "INET" | "MACADDR8" | "DATE" | "TIME" | "TIMESTAMP" | "TIMESTAMPTZ" | "INTERVAL" | "TIMETZ" | "BIT" | "VARBIT" | "ACLITEM" | "PG_DATABASE" | (string & {}); /** * An array parameter created by {@link SQL.array} */ interface SQLArrayParameter { /** * The array values, serialized into a single string */ serializedValues: string; /** * The element type of the array, for example `"INT"` */ arrayType: ArrayType; } /** * The client passed to transaction callbacks ({@link SQL.begin}, * {@link SQL.transaction}). Extends {@link SQL} with savepoints. */ interface TransactionSQL extends SQL { /** * Creates a savepoint within the current transaction */ savepoint(name: string, fn: SQL.SavepointContextCallback): Promise; savepoint(fn: SQL.SavepointContextCallback): Promise; /** * Reserves a connection from the pool and returns a client that wraps * that single connection. * * Inside a transaction, `reserve()` returns a brand new connection, not * one related to the transaction. This matches the behaviour of the * `postgres` package. */ reserve(): Promise; } namespace SQL { class SQLError extends Error { constructor(message: string); } class PostgresError extends SQLError { public readonly code: string; public readonly errno?: string | undefined; public readonly detail?: string | undefined; public readonly hint?: string | undefined; public readonly severity?: string | undefined; public readonly position?: string | undefined; public readonly internalPosition?: string | undefined; public readonly internalQuery?: string | undefined; public readonly where?: string | undefined; public readonly schema?: string | undefined; public readonly table?: string | undefined; public readonly column?: string | undefined; public readonly dataType?: string | undefined; public readonly constraint?: string | undefined; public readonly file?: string | undefined; public readonly line?: string | undefined; public readonly routine?: string | undefined; constructor( message: string, options: { code: string; errno?: string | undefined; detail?: string; hint?: string | undefined; severity?: string | undefined; position?: string | undefined; internalPosition?: string; internalQuery?: string; where?: string | undefined; schema?: string; table?: string | undefined; column?: string | undefined; dataType?: string | undefined; constraint?: string; file?: string | undefined; line?: string | undefined; routine?: string | undefined; }, ); } class MySQLError extends SQLError { public readonly code: string; public readonly errno?: number | undefined; public readonly sqlState?: string | undefined; constructor(message: string, options: { code: string; errno: number | undefined; sqlState: string | undefined }); } class SQLiteError extends SQLError { public readonly code: string; public readonly errno: number; public readonly byteOffset?: number | undefined; constructor(message: string, options: { code: string; errno: number; byteOffset?: number | undefined }); } type AwaitPromisesArray>> = { [K in keyof T]: Awaited; }; type ContextCallbackResult = T extends Array> ? AwaitPromisesArray : Awaited; type ContextCallback = (sql: SQL) => Bun.MaybePromise; interface SQLiteOptions extends BunSQLite.DatabaseOptions { adapter?: "sqlite"; /** * Path to the database file * * Examples: * * - `sqlite://:memory:` * - `sqlite://./path/to/database.db` * - `sqlite:///Users/bun/projects/my-app/database.db` * - `./dev.db` * - `:memory:` * * @default ":memory:" */ filename?: URL | ":memory:" | (string & {}) | undefined; /** * Called when a connection attempt completes. * Receives an `Error` on failure, or `null` on success. */ onconnect?: ((err: Error | null) => void) | undefined; /** * Called when a connection is closed. * Receives the closing `Error`, or `null`. */ onclose?: ((err: Error | null) => void) | undefined; } interface PostgresOrMySQLOptions { /** * Connection URL, for example `postgres://user:pass@localhost:5432/mydb` */ url?: URL | string | undefined; /** * Database server hostname * @deprecated Prefer {@link hostname} * @default "localhost" */ host?: string | undefined; /** * Database server hostname * @default "localhost" */ hostname?: string | undefined; /** * Database server port number * @default 5432 */ port?: number | string | undefined; /** * Database user for authentication * @default "postgres" */ username?: string | undefined; /** * Database user for authentication (alias for username) * @deprecated Prefer {@link username} * @default "postgres" */ user?: string | undefined; /** * Database password for authentication * @default "" */ password?: string | (() => MaybePromise) | undefined; /** * Database password for authentication (alias for password) * @deprecated Prefer {@link password} * @default "" */ pass?: string | (() => MaybePromise) | undefined; /** * Name of the database to connect to * @default The username value */ database?: string | undefined; /** * Name of the database to connect to (alias for database) * @deprecated Prefer {@link database} * @default The username value */ db?: string | undefined; /** * Database adapter/driver to use * @default "postgres" */ adapter?: "postgres" | "mysql" | "mariadb"; /** * Maximum time in seconds a connection can sit idle before it is closed * @default 0 (no timeout) */ idleTimeout?: number | undefined; /** * Maximum time in seconds a connection can sit idle before it is closed (alias for idleTimeout) * @deprecated Prefer {@link idleTimeout} * @default 0 (no timeout) */ idle_timeout?: number | undefined; /** * Maximum time in seconds to wait when establishing a connection * @default 30 */ connectionTimeout?: number | undefined; /** * Maximum time in seconds to wait when establishing a connection (alias for connectionTimeout) * @deprecated Prefer {@link connectionTimeout} * @default 30 */ connection_timeout?: number | undefined; /** * Maximum time in seconds to wait when establishing a connection (alias * for connectionTimeout) * @deprecated Prefer {@link connectionTimeout} * @default 30 */ connectTimeout?: number | undefined; /** * Maximum time in seconds to wait when establishing a connection (alias * for connectionTimeout) * @deprecated Prefer {@link connectionTimeout} * @default 30 */ connect_timeout?: number | undefined; /** * Maximum lifetime in seconds of a connection * @default 0 (no maximum lifetime) */ maxLifetime?: number | undefined; /** * Maximum lifetime in seconds of a connection (alias for maxLifetime) * @deprecated Prefer {@link maxLifetime} * @default 0 (no maximum lifetime) */ max_lifetime?: number | undefined; /** * Whether to use TLS/SSL for the connection. A string selects the * SSL mode (`"disable"`, `"allow"`, `"prefer"`, `"require"`, `"verify-ca"`, `"verify-full"`). * @default false */ tls?: | Bun.BunFile | TLSOptions | boolean | "disable" | "allow" | "prefer" | "require" | "verify-ca" | "verify-full" | undefined; /** * Whether to use TLS/SSL for the connection (alias for tls) * @deprecated Prefer {@link tls} * @default false */ ssl?: | Bun.BunFile | TLSOptions | boolean | "disable" | "allow" | "prefer" | "require" | "verify-ca" | "verify-full" | undefined; /** * Unix domain socket path for connection * @default undefined */ path?: string | undefined; /** * Called when a connection attempt completes. * Receives an `Error` on failure, or `null` on success. */ onconnect?: ((err: Error | null) => void) | undefined; /** * Called when a connection is closed. * Receives the closing `Error`, or `null`. */ onclose?: ((err: Error | null) => void) | undefined; /** * Postgres client runtime configuration options * * @see https://www.postgresql.org/docs/current/runtime-config-client.html */ connection?: Record | undefined; /** * Maximum number of connections in the pool * @default 10 */ max?: number | undefined; /** * Return values outside the i32 range as `BigInt`. By default they are * returned as strings. * @default false */ bigint?: boolean | undefined; /** * Automatic creation of prepared statements * @default true */ prepare?: boolean | undefined; /** * MySQL only. Allow the client to request the server's RSA public key * during `caching_sha2_password` / `sha256_password` authentication when * the connection is not protected by TLS. Disabled by default because a * network attacker can substitute their own key and recover the * plaintext password. Enable only for trusted local connections, or use * TLS instead. * @default false */ allowPublicKeyRetrieval?: boolean | undefined; } /** * Configuration options for SQL client connection and behavior * * @example * ```ts * const config: Bun.SQL.Options = { * host: 'localhost', * port: 5432, * user: 'dbuser', * password: 'secretpass', * database: 'myapp', * idleTimeout: 30, * max: 20, * onconnect: (err) => { * if (!err) console.log('Connected to database'); * } * }; * ``` */ type Options = SQLiteOptions | PostgresOrMySQLOptions; /** One registration made by {@link SQL.listen}. */ interface ListenSubscription extends AsyncDisposable { readonly channel: string; /** * Remove this registration. Resolves once the channel is no longer * subscribed, or immediately when other registrations on it remain. * Idempotent; `await using` calls it at the end of the scope. */ unlisten(): Promise; } /** * A pending SQL query. Extends `Promise`, so it can be awaited, and adds * methods to control how it runs. */ interface Query extends Promise { /** * True while the query is executing */ active: boolean; /** * True if the query has been cancelled */ cancelled: boolean; /** * Cancels the executing query */ cancel(): Query; /** * Executes the query as a simple query. Parameters are not allowed, but * the query can contain multiple commands separated by semicolons. */ simple(): Query; /** * Starts executing the query. Queries are lazy: they only run when * awaited or executed with this method. */ execute(): Query; /** * Returns rows as arrays of `Buffer` objects instead of objects */ raw(): Query; /** * Returns each row as an array of values, in the same order as the * columns in the query */ values(): Query; } /** * Callback function type for transaction contexts * @param sql Function to execute SQL queries within the transaction */ type TransactionContextCallback = ContextCallback; /** * Callback function type for savepoint contexts * @param sql Function to execute SQL queries within the savepoint */ type SavepointContextCallback = ContextCallback; /** * A parameter or serializable value interpolated into a query. * * @example * ```ts * const helper = sql(users, 'id'); * await sql`insert into users ${helper}`; * ``` */ interface Helper { readonly value: T[]; readonly columns: (keyof T)[]; } } interface SQL extends AsyncDisposable { /** * Executes a SQL query using template literals * @example * ```ts * const [user] = await sql`select * from users where id = ${1}`; * ``` */ (strings: TemplateStringsArray, ...values: unknown[]): SQL.Query; /** * Executes a SQL query from a string * * @example * ```ts * const users = await sql("SELECT * FROM users"); * ``` */ (string: string): SQL.Query; /** * Helper function for inserting an object into a query * * @example * ```ts * // Insert an object * const result = await sql`insert into users ${sql(users)} returning *`; * * // Or pick specific columns * const result = await sql`insert into users ${sql(users, "id", "name")} returning *`; * * // Or a single object * const result = await sql`insert into users ${sql(user)} returning *`; * ``` */ (obj: T | T[] | readonly T[]): SQL.Helper; // Contributor note: This is the same as the signature below with the exception of the columns and the Pick /** * Helper function for inserting an object into a query, supporting specific columns * * @example * ```ts * // Insert an object * const result = await sql`insert into users ${sql(users)} returning *`; * * // Or pick specific columns * const result = await sql`insert into users ${sql(users, "id", "name")} returning *`; * * // Or a single object * const result = await sql`insert into users ${sql(user)} returning *`; * ``` */ ( obj: T | T[] | readonly T[], ...columns: readonly Keys[] ): SQL.Helper>; // Contributor note: This is the same as the signature above with the exception of this signature tracking keys /** * Helper function for inserting any serializable value into a query * * @example * ```ts * const result = await sql`SELECT * FROM users WHERE id IN ${sql([1, 2, 3])}`; * ``` */ // `T & {}` rejects a bare `null`/`undefined` value, which the runtime // throws on. An array such as `[null]` stays allowed: it is a valid // `WHERE IN` binding and is an object, so it still satisfies `{}`. (value: T & {}): SQL.Helper; } /** * SQL client. Manages a connection pool, queries, and transactions. */ class SQL { /** * Creates a new SQL client instance * * @param connectionString Database connection string or URL * * @example * ```ts * const sql = new SQL("postgres://localhost:5432/mydb"); * const sql = new SQL(new URL("postgres://localhost:5432/mydb")); * ``` */ constructor(connectionString: string | URL); /** * Creates a new SQL client instance with options * * @param connectionString Database connection string or URL * @param options Connection and pool options * * @example * ```ts * const sql = new SQL("postgres://localhost:5432/mydb", { idleTimeout: 1000 }); * ``` */ constructor( connectionString: string | URL, options: Bun.__internal.DistributedOmit, ); /** * Creates a new SQL client instance with options * * @param options Connection and pool options, including the URL or filename * * @example * ```ts * const sql = new SQL({ url: "postgres://localhost:5432/mydb", idleTimeout: 1000 }); * ``` */ constructor(options?: SQL.Options); /** * Current client options */ options: Bun.__internal.DistributedMerge; /** * Commits a distributed transaction, also known as a prepared transaction * in PostgreSQL or an XA transaction in MySQL * * @param name Name of the distributed transaction * * @throws {Error} If the adapter does not support distributed transactions (e.g., SQLite) * * @example * ```ts * await sql.commitDistributed("my_distributed_transaction"); * ``` */ commitDistributed(name: string): Promise; /** * Rolls back a distributed transaction, also known as a prepared * transaction in PostgreSQL or an XA transaction in MySQL * * @param name Name of the distributed transaction * * @throws {Error} If the adapter does not support distributed transactions (e.g., SQLite) * * @example * ```ts * await sql.rollbackDistributed("my_distributed_transaction"); * ``` */ rollbackDistributed(name: string): Promise; /** Waits for the database connection to be established * * @example * ```ts * await sql.connect(); * ``` */ connect(): Promise; /** * Closes the database connection. With `timeout: 0` it closes * immediately; with no timeout it waits for all queries to finish first. * * @param options Optional `timeout` in seconds * * @example * ```ts * await sql.close({ timeout: 1 }); * ``` */ close(options?: { timeout?: number }): Promise; /** * Closes the database connection. Alias of {@link SQL.close}. * * @param options Optional `timeout` in seconds * * @example * ```ts * await sql.end({ timeout: 1 }); * ``` */ end(options?: { timeout?: number }): Promise; /** * Flushes any pending operations * * @throws {Error} If the adapter does not support flushing (e.g., SQLite) * * @example * ```ts * sql.flush(); * ``` */ flush(): void; /** * Reserves a connection from the pool and returns a client that wraps * that single connection. Use it to run queries on an isolated * connection. * * Calling `reserve()` on a reserved client returns a new reserved * connection, not the same one (behavior matches the `postgres` package). * * @throws {Error} If the adapter does not support connection pooling (e.g., SQLite) * * @example * ```ts * const reserved = await sql.reserve(); * await reserved`select * from users`; * await reserved.release(); * * // In production, release in a finally block * const reserved = await sql.reserve(); * try { * // ... queries * } finally { * await reserved.release(); * } * * // Bun supports Symbol.dispose and Symbol.asyncDispose, * // so `using` releases the connection at the end of the scope * using reserved = await sql.reserve() * await reserved`select * from users` * ``` */ reserve(): Promise; /** * Creates a SQL array parameter * @param values Array values to bind * @param typeNameOrTypeID Element type name or type ID; defaults to JSON when omitted * @returns The array parameter, ready to interpolate into a query * * @example * ```ts * const array = sql.array([1, 2, 3], "INT"); * await sql`CREATE TABLE users_posts (user_id INT, posts_id INT[])`; * await sql`INSERT INTO users_posts (user_id, posts_id) VALUES (${user.id}, ${array})`; * ``` */ array(values: any[], typeNameOrTypeID?: number | ArrayType): SQLArrayParameter; /** * Begins a new transaction. * * Reserves a connection for the transaction and passes a scoped `sql` * instance to the callback. `sql.begin` resolves with the callback's * return value. `BEGIN` is sent automatically, and if anything fails, * `ROLLBACK` is sent so the connection can be released and execution can * continue. * @example * ```ts * const [user, account] = await sql.begin(async sql => { * const [user] = await sql` * insert into users ( * name * ) values ( * 'Murray' * ) * returning * * ` * const [account] = await sql` * insert into accounts ( * user_id * ) values ( * ${ user.user_id } * ) * returning * * ` * return [user, account] * }) * ``` */ begin(fn: SQL.TransactionContextCallback): Promise>; /** * Begins a new transaction with options. * * Reserves a connection for the transaction and passes a scoped `sql` * instance to the callback. `sql.begin` resolves with the callback's * return value. `BEGIN` is sent with the given options, and if anything * fails, `ROLLBACK` is sent so the connection can be released and * execution can continue. * @example * ```ts * const [user, account] = await sql.begin("read write", async sql => { * const [user] = await sql` * insert into users ( * name * ) values ( * 'Murray' * ) * returning * * ` * const [account] = await sql` * insert into accounts ( * user_id * ) values ( * ${ user.user_id } * ) * returning * * ` * return [user, account] * }) * ``` */ begin(options: string, fn: SQL.TransactionContextCallback): Promise>; /** * Begins a new transaction. Alias of {@link begin}. * * Reserves a connection for the transaction and passes a scoped `sql` * instance to the callback. `sql.transaction` resolves with the * callback's return value. `BEGIN` is sent automatically, and if * anything fails, `ROLLBACK` is sent so the connection can be released * and execution can continue. * @alias begin * @example * ```ts * const [user, account] = await sql.transaction(async sql => { * const [user] = await sql` * insert into users ( * name * ) values ( * 'Murray' * ) * returning * * ` * const [account] = await sql` * insert into accounts ( * user_id * ) values ( * ${ user.user_id } * ) * returning * * ` * return [user, account] * }) * ``` */ transaction(fn: SQL.TransactionContextCallback): Promise>; /** * Begins a new transaction with options. Alias of {@link begin}. * * Reserves a connection for the transaction and passes a scoped `sql` * instance to the callback. `sql.transaction` resolves with the * callback's return value. `BEGIN` is sent with the given options, and * if anything fails, `ROLLBACK` is sent so the connection can be * released and execution can continue. * * @alias {@link begin} * * @example * ```ts * const [user, account] = await sql.transaction("read write", async sql => { * const [user] = await sql` * insert into users ( * name * ) values ( * 'Murray' * ) * returning * * ` * const [account] = await sql` * insert into accounts ( * user_id * ) values ( * ${ user.user_id } * ) * returning * * ` * return [user, account] * }); * ``` */ transaction(options: string, fn: SQL.TransactionContextCallback): Promise>; /** * Begins a distributed transaction, also known as Two-Phase Commit. In * phase 1 the coordinator prepares each node, making sure its data is * written and ready to commit; in phase 2 the nodes commit or roll back * based on the coordinator's decision, ensuring durability and releasing * locks. * * `beginDistributed` rolls back automatically if an exception is not * caught; otherwise you commit or roll back later with * {@link commitDistributed} or {@link rollbackDistributed}. * * In PostgreSQL and MySQL, distributed transactions persist beyond the * original session, so privileged users or coordinators can commit or * roll them back later, which supports recovery and administrative * tasks. PostgreSQL implements them with `PREPARE TRANSACTION`; MySQL * uses XA Transactions. MSSQL also supports distributed/XA transactions, * but ties them to the original session, the DTC coordinator, and the * specific connection: they are committed or rolled back under the same * rules as regular transactions, with no manual intervention from other * sessions, and are used to coordinate transactions across Linked * Servers. * * @throws {Error} If the adapter does not support distributed transactions (e.g., SQLite) * * @example * ```ts * await sql.beginDistributed("numbers", async sql => { * await sql`create table if not exists numbers (a int)`; * await sql`insert into numbers values(1)`; * }); * // later you can call * await sql.commitDistributed("numbers"); * // or await sql.rollbackDistributed("numbers"); * ``` */ beginDistributed( name: string, fn: SQL.TransactionContextCallback, ): Promise>; /** Begins a distributed transaction. Alias of {@link beginDistributed}. * @alias {@link beginDistributed} */ distributed(name: string, fn: SQL.TransactionContextCallback): Promise>; /** * Executes any query string as-is. This can lead to SQL injection if the * string contains untrusted input. * * `sql.unsafe` can be nested inside a safe `sql` expression, for example * when only part of the query is unsafe. * * With the SQLite adapter, `values` may also be an object of named * parameters (`:name`, `$name`, or `@name` placeholders). Object keys * keep the prefix unless the connection sets `strict: true`. * @example * ```ts * const result = await sql.unsafe(`select ${danger} from users where id = ${dragons}`) * const row = await sql.unsafe("select * from users where id = :id", { ":id": 1 }) * ``` */ unsafe(string: string, values?: any[] | Record): SQL.Query; /** * Reads a file and runs its contents as a query. * Pass `values` if the file uses positional parameters (`$1`, `$2`, ...). * With the SQLite adapter, `values` may also be an object of named * parameters (`:name`, `$name`, or `@name` placeholders); keys keep the * prefix unless the connection sets `strict: true`. * @example * ```ts * const result = await sql.file("query.sql", [1, 2, 3]); * ``` */ file(filename: string, values?: any[] | Record): SQL.Query; /** * Subscribe to a PostgreSQL `LISTEN` channel. Resolves once the server has * acknowledged the subscription, with a handle that removes it again. * * Every call is its own registration: several on one channel share a * single server-side subscription and each receives every notification. * All of them share one dedicated connection, opened by the first * `listen()` and closed when the last registration is removed. If it * drops, it is reconnected with exponential backoff and every channel is * re-subscribed; `onlisten` runs again each time. * * A throwing `onnotify` or `onlisten` is reported as an uncaught exception. * * @param channel - Channel name, quoted for you; at most 63 bytes, the * PostgreSQL identifier limit * @param onnotify - Receives each notification's payload * @param onlisten - Runs once the `LISTEN` is acknowledged, initially and * after every reconnect * * @example * ```ts * const subscription = await sql.listen("events", payload => console.log(payload)); * await sql.notify("events", "hello"); * await subscription.unlisten(); * ``` * * @example * ```ts * await using subscription = await sql.listen("events", handle); * ``` */ listen( channel: string, onnotify: (payload: string) => void, onlisten?: () => void, ): Promise; /** * Send a PostgreSQL `NOTIFY` via `pg_notify`. Runs as a normal query on * this handle, so on a `sql.begin()` transaction it is delivered on commit * and discarded on rollback. Omitting `payload` sends an empty one, like a * bare `NOTIFY channel`. * * @example * ```ts * await sql.notify("events", JSON.stringify({ id: 1 })); * await sql.notify("cache-invalidated"); * ``` */ notify(channel: string, payload?: string): Promise; } /** * The default SQL client, configured from environment variables such as * `DATABASE_URL` */ const sql: SQL; /** * SQL client for PostgreSQL * * @deprecated Prefer {@link Bun.sql} */ const postgres: SQL; /** * The client passed to {@link TransactionSQL.savepoint} callbacks; queries * run within that savepoint */ interface SavepointSQL extends SQL {} }