> ## Documentation Index
> Fetch the complete documentation index at: https://bun.ll1025.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# SQL

> Bun 通过统一的基于 Promise 的 API 提供了处理 SQL 数据库的原生绑定，支持 PostgreSQL、MySQL 和 SQLite。

查询以标签模板字面量形式编写，客户端支持连接池、事务和预编译语句。

```ts title="db.ts" icon="https://mintcdn.com/span-inc/82N53aP7NFbaCVSl/icons/typescript.svg?fit=max&auto=format&n=82N53aP7NFbaCVSl&q=85&s=787d39d6a7d96d7f9540dc74344eba23" theme={null}
import { sql, SQL } from "bun";

// PostgreSQL（默认）
const users = await sql`
  SELECT * FROM users
  WHERE active = ${true}
  LIMIT ${10}
`;

// 使用 MySQL
const mysql = new SQL("mysql://user:pass@localhost:3306/mydb");
const mysqlResults = await mysql`
  SELECT * FROM users 
  WHERE active = ${true}
`;

// 使用 SQLite
const sqlite = new SQL("sqlite://myapp.db");
const sqliteResults = await sqlite`
  SELECT * FROM users 
  WHERE active = ${1}
`;
```

### 特性

* 标签模板字面量可防止 SQL 注入
* 事务
* 命名参数和位置参数
* 连接池
* `BigInt` 支持
* SASL（SCRAM-SHA-256）、MD5 和明文认证
* 连接超时
* 返回数据对象、数组的数组或 Buffer 格式的行
* 二进制协议支持使其速度更快
* TLS 支持（和认证模式）
* 使用环境变量自动配置

***

## 数据库支持

`Bun.SQL` 为多种数据库系统提供统一的 API：

### PostgreSQL

PostgreSQL 在以下情况下使用：

* 连接字符串不匹配 SQLite 或 MySQL 模式（它是回退适配器）
* 连接字符串显式使用 `postgres://` 或 `postgresql://` 协议
* 未提供连接字符串且环境变量指向 PostgreSQL

```ts title="db.ts" icon="https://mintcdn.com/span-inc/82N53aP7NFbaCVSl/icons/typescript.svg?fit=max&auto=format&n=82N53aP7NFbaCVSl&q=85&s=787d39d6a7d96d7f9540dc74344eba23" theme={null}
import { sql } from "bun";
// 如果 DATABASE_URL 未设置或为 PostgreSQL URL，则使用 PostgreSQL
await sql`SELECT ...`;

import { SQL } from "bun";
const pg = new SQL("postgres://user:pass@localhost:5432/mydb");
await pg`SELECT ...`;
```

### MySQL

MySQL 支持内置于 `Bun.SQL` 中，使用相同的标签模板字面量接口，兼容 MySQL 5.7+ 和 MySQL 8.0+：

```ts title="db.ts" icon="https://mintcdn.com/span-inc/82N53aP7NFbaCVSl/icons/typescript.svg?fit=max&auto=format&n=82N53aP7NFbaCVSl&q=85&s=787d39d6a7d96d7f9540dc74344eba23" theme={null}
import { SQL } from "bun";

// MySQL 连接
const mysql = new SQL("mysql://user:password@localhost:3306/database");
const mysql2 = new SQL("mysql2://user:password@localhost:3306/database"); // mysql2 协议也有效

// 使用选项对象
const mysql3 = new SQL({
  adapter: "mysql",
  hostname: "localhost",
  port: 3306,
  database: "myapp",
  username: "dbuser",
  password: "secretpass",
});

// 支持参数 - 自动使用预编译语句
const users = await mysql`SELECT * FROM users WHERE id = ${userId}`;

// 事务与 PostgreSQL 相同
await mysql.begin(async tx => {
  await tx`INSERT INTO users (name) VALUES (${"Alice"})`;
  await tx`UPDATE accounts SET balance = balance - 100 WHERE user_id = ${userId}`;
});

// 批量插入
const newUsers = [
  { name: "Alice", email: "alice@example.com" },
  { name: "Bob", email: "bob@example.com" },
];
await mysql`INSERT INTO users ${mysql(newUsers)}`;
```

<Accordion title="MySQL 连接字符串格式">
  MySQL 接受多种 URL 格式的连接字符串：

  ```ts theme={null}
  // 标准 mysql:// 协议
  new SQL("mysql://user:pass@localhost:3306/database");
  new SQL("mysql://user:pass@localhost/database"); // 默认端口 3306

  // mysql2:// 协议（与 mysql2 npm 包兼容）
  new SQL("mysql2://user:pass@localhost:3306/database");

  // 带查询参数
  new SQL("mysql://user:pass@localhost/db?ssl=true");

  // Unix 套接字连接
  new SQL("mysql://user:pass@/database?socket=/var/run/mysqld/mysqld.sock");
  ```
</Accordion>

<Accordion title="MySQL 特有功能">
  MySQL 数据库支持：

  * **预编译语句**：为参数化查询自动创建，并带有语句缓存
  * **二进制协议**：用于更好的预编译语句性能和精确的类型处理
  * **多结果集**：支持返回多个结果集的存储过程
  * **认证插件**：支持 mysql\_native\_password、caching\_sha2\_password（MySQL 8.0 默认）和 sha256\_password
  * **SSL/TLS 连接**：可配置的 SSL 模式，类似 PostgreSQL
  * **连接属性**：发送到服务器的客户端信息，用于监控
  * **查询流水线**：无需等待响应即可执行多个预编译语句
</Accordion>

### SQLite

SQLite 支持内置于 `Bun.SQL` 中，使用相同的标签模板字面量接口：

```ts theme={null}
import { SQL } from "bun";

// 内存数据库
const memory = new SQL(":memory:");
const memory2 = new SQL("sqlite://:memory:");

// 基于文件的数据库
const sql1 = new SQL("sqlite://myapp.db");

// 使用选项对象
const sql2 = new SQL({
  adapter: "sqlite",
  filename: "./data/app.db",
});

// 对于简单的文件名，显式指定适配器
const sql3 = new SQL("myapp.db", { adapter: "sqlite" });
```

<Accordion title="SQLite 连接字符串格式">
  SQLite 接受多种 URL 格式的连接字符串：

  ```ts theme={null}
  // 标准 sqlite:// 协议
  new SQL("sqlite://path/to/database.db");
  new SQL("sqlite:path/to/database.db"); // 不带斜杠

  // file:// 协议（也被识别为 SQLite）
  new SQL("file://path/to/database.db");
  new SQL("file:path/to/database.db");

  // 特殊的 :memory: 数据库
  new SQL(":memory:");
  new SQL("sqlite://:memory:");
  new SQL("file://:memory:");

  // 相对和绝对路径
  new SQL("sqlite://./local.db"); // 相对于当前目录
  new SQL("sqlite://../parent/db.db"); // 父目录
  new SQL("sqlite:///absolute/path.db"); // 绝对路径

  // 带查询参数
  new SQL("sqlite://data.db?mode=ro"); // 只读模式
  new SQL("sqlite://data.db?mode=rw"); // 读写模式（不创建）
  new SQL("sqlite://data.db?mode=rwc"); // 读写创建模式（默认）
  ```

  <Note>
    不带协议的文件名（如 `"myapp.db"`）需要显式指定 `{ adapter: "sqlite" }`，以避免与 PostgreSQL 产生歧义。
  </Note>
</Accordion>

<Accordion title="SQLite 特有选项">
  SQLite 数据库支持额外的配置选项：

  ```ts theme={null}
  const sql = new SQL({
    adapter: "sqlite",
    filename: "app.db",

    // SQLite 特有选项
    readonly: false, // 以只读模式打开
    create: true, // 如果数据库不存在则创建
    readwrite: true, // 以读写模式打开

    // 额外的 Bun:sqlite 选项
    strict: true, // 启用严格模式
    safeIntegers: false, // 对整数使用 JavaScript 数字
  });
  ```

  URL 中的查询参数会被解析以设置这些选项：

  * `?mode=ro` → `readonly: true`
  * `?mode=rw` → `readonly: false, create: false`
  * `?mode=rwc` → `readonly: false, create: true`（默认）
</Accordion>

## 插入数据

直接将 JavaScript 值传递给 SQL 模板字面量；Bun 负责转义。

```ts theme={null}
import { sql } from "bun";

// 使用直接值的基本插入
const [user] = await sql`
  INSERT INTO users (name, email) 
  VALUES (${name}, ${email})
  RETURNING *
`;

// 使用对象助手获得更简洁的语法
const userData = {
  name: "Alice",
  email: "alice@example.com",
};

const [newUser] = await sql`
  INSERT INTO users ${sql(userData)}
  RETURNING *
`;
// 扩展为：INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')
```

### 批量插入

你也可以传递一个对象数组，Bun 会将其扩展为 `INSERT INTO ... VALUES ...` 语句。

```ts theme={null}
const users = [
  { name: "Alice", email: "alice@example.com" },
  { name: "Bob", email: "bob@example.com" },
  { name: "Charlie", email: "charlie@example.com" },
];

await sql`INSERT INTO users ${sql(users)}`;
```

### 选择要插入的列

使用 `sql(object, ...string)` 选择要插入的列。每个列必须在对象上定义。

```ts theme={null}
const user = {
  name: "Alice",
  email: "alice@example.com",
  age: 25,
};

await sql`INSERT INTO users ${sql(user, "name", "email")}`;
// 只插入 name 和 email 列，忽略其他字段
```

***

## 查询结果

默认情况下，Bun 的 SQL 客户端以对象数组形式返回查询结果，每个对象代表一行，列名作为键。还有两种其他格式可用。

### `sql``.values()` 格式

`sql``.values()` 方法将每一行作为值数组返回，顺序与查询中的列相同。

```ts theme={null}
const rows = await sql`SELECT * FROM users`.values();
console.log(rows);
```

结果看起来像：

```ts theme={null}
[
  ["Alice", "alice@example.com"],
  ["Bob", "bob@example.com"],
];
```

当查询返回重复的列名时，`sql``.values()` 非常有用。使用对象（默认）时，因为列名是键，所以最后一个列会胜出。使用 `sql``.values()` 时，每个列都出现在数组中，因此你可以按索引读取重复列。

### `sql``.raw()` 格式

`.raw()` 方法将行作为 `Buffer` 对象的数组返回。用于二进制数据或性能优化。

```ts theme={null}
const rows = await sql`SELECT * FROM users`.raw();
console.log(rows); // [[Buffer, Buffer], [Buffer, Buffer], [Buffer, Buffer]]
```

***

## SQL 片段

Bun 可以根据运行时条件动态构建查询，而不会有 SQL 注入风险。

### 动态表名

要动态引用表或模式，使用 `sql()` 助手进行转义：

```ts theme={null}
// 安全地动态引用表
await sql`SELECT * FROM ${sql("users")}`;

// 带模式限定
await sql`SELECT * FROM ${sql("public.users")}`;
```

### 条件查询

使用 `sql()` 助手构建带条件子句的查询：

```ts theme={null}
// 可选的 WHERE 子句
const filterAge = true;
const minAge = 21;
const ageFilter = sql`AND age > ${minAge}`;
await sql`
  SELECT * FROM users
  WHERE active = ${true}
  ${filterAge ? ageFilter : sql``}
`;
```

### 更新中的动态列

使用 `sql(object, ...string)` 选择要更新的列。每个列必须在对象上定义。如果不列出任何列，则使用对象上的所有键。

```ts theme={null}
await sql`UPDATE users SET ${sql(user, "name", "email")} WHERE id = ${user.id}`;
// 使用对象的所有键更新行
await sql`UPDATE users SET ${sql(user)} WHERE id = ${user.id}`;
```

### 动态值和 `where in`

值列表也可以动态创建，用于 `WHERE IN` 查询。你也可以传递一个对象数组并指定键，以从中构建列表。

```ts theme={null}
await sql`SELECT * FROM users WHERE id IN ${sql([1, 2, 3])}`;

const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 3, name: "Charlie" },
];
await sql`SELECT * FROM users WHERE id IN ${sql(users, "id")}`;
```

### `sql.array` 助手

`sql.array` 助手从 JavaScript 数组创建 PostgreSQL 数组字面量：

```ts theme={null}
// 为 PostgreSQL 创建数组字面量
await sql`INSERT INTO tags (items) VALUES (${sql.array(["red", "blue", "green"])})`;
// 生成：INSERT INTO tags (items) VALUES (ARRAY['red', 'blue', 'green'])

// 也适用于数值数组
await sql`SELECT * FROM products WHERE ids = ANY(${sql.array([1, 2, 3])})`;
// 生成：SELECT * FROM products WHERE ids = ANY(ARRAY[1, 2, 3])
```

<Note>`sql.array` 仅适用于 PostgreSQL。多维数组和 NULL 元素可能尚不支持。</Note>

***

## `sql``.simple()`

PostgreSQL 有线协议支持两种类型的查询："simple"和"extended"。Simple 查询可以包含多个语句但不支持参数，而 extended 查询（默认）支持参数但只允许一个语句。

要在单个查询中运行多个语句，使用 `sql``.simple()`：

```ts theme={null}
// 一个查询中的多个语句
await sql`
  SELECT 1;
  SELECT 2;
`.simple();
```

Simple 查询适用于数据库迁移和设置脚本。

Simple 查询不能使用参数（`${value}`）。如果你需要参数，请将查询拆分为单独的语句。

### 文件中的查询

`sql.file` 从文件读取查询并执行。如果文件使用 `$1` 和 `$2` 等占位符，你可以向查询传递参数。不带参数时，文件可以包含多个命令。

```ts theme={null}
const result = await sql.file("query.sql", [1, 2, 3]);
```

### 不安全查询

`sql.unsafe` 执行原始 SQL 字符串。谨慎使用：它不会转义用户输入。不带参数时，字符串可以包含多个命令。

```ts theme={null}
// 不带参数的多个命令
const result = await sql.unsafe(`
  SELECT ${userColumns} FROM users;
  SELECT ${accountColumns} FROM accounts;
`);

// 使用参数（只允许一个命令）
const result = await sql.unsafe("SELECT " + dangerous + " FROM users WHERE id = $1", [id]);
```

### 执行和取消查询

查询是惰性的：它们只有在被 await 或使用 `.execute()` 运行时才会开始执行。
要取消正在运行的查询，在查询对象上调用 `cancel()`。

```ts theme={null}
const query = sql`SELECT * FROM users`.execute();
setTimeout(() => query.cancel(), 100);
await query;
```

***

## 数据库环境变量

你可以使用环境变量配置 `sql` 连接参数。客户端按优先级顺序检查它们，并从连接字符串格式检测数据库类型。

### 自动数据库检测

当你使用无参数的 `Bun.sql()` 或带连接字符串的 `new SQL()` 时，Bun 会从 URL 格式检测适配器：

#### MySQL 自动检测

MySQL 在连接字符串匹配以下模式时被选择：

* `mysql://...` - MySQL 协议 URL
* `mysql2://...` - MySQL2 协议 URL（兼容别名）

```ts theme={null}
// 这些都会自动使用 MySQL（无需适配器）
const sql1 = new SQL("mysql://user:pass@localhost/mydb");
const sql2 = new SQL("mysql2://user:pass@localhost:3306/mydb");

// 与 DATABASE_URL 环境变量一起使用
DATABASE_URL="mysql://user:pass@localhost/mydb" bun run app.js
DATABASE_URL="mysql2://user:pass@localhost:3306/mydb" bun run app.js
```

#### SQLite 自动检测

SQLite 在连接字符串匹配以下模式时被选择：

* `:memory:` - 内存数据库
* `sqlite://...` - SQLite 协议 URL
* `sqlite:...` - 不带斜杠的 SQLite 协议
* `file://...` - 文件协议 URL
* `file:...` - 不带斜杠的文件协议

```ts theme={null}
// 这些都会自动使用 SQLite（无需适配器）
const sql1 = new SQL(":memory:");
const sql2 = new SQL("sqlite://app.db");
const sql3 = new SQL("file://./database.db");

// 与 DATABASE_URL 环境变量一起使用
DATABASE_URL=":memory:" bun run app.js
DATABASE_URL="sqlite://myapp.db" bun run app.js
DATABASE_URL="file://./data/app.db" bun run app.js
```

#### PostgreSQL 自动检测

PostgreSQL 是不匹配 MySQL 或 SQLite 模式的连接字符串的默认值：

```bash theme={null}
# 这些模式会被检测为 PostgreSQL
DATABASE_URL="postgres://user:pass@localhost:5432/mydb" bun run app.js
DATABASE_URL="postgresql://user:pass@localhost:5432/mydb" bun run app.js

# 或不匹配 MySQL 或 SQLite 模式的任何 URL
DATABASE_URL="localhost:5432/mydb" bun run app.js
```

### MySQL 环境变量

MySQL 连接可以使用环境变量配置：

```bash theme={null}
# 主要连接 URL（首先检查）
MYSQL_URL="mysql://user:pass@localhost:3306/mydb"

# 备选：带 MySQL 协议的 DATABASE_URL
DATABASE_URL="mysql://user:pass@localhost:3306/mydb"
DATABASE_URL="mysql2://user:pass@localhost:3306/mydb"
```

如果未提供连接 URL，Bun 会检查这些单独的参数：

| 环境变量                     | 默认值         | 描述                 |
| ------------------------ | ----------- | ------------------ |
| `MYSQL_HOST`             | `localhost` | 数据库主机              |
| `MYSQL_PORT`             | `3306`      | 数据库端口              |
| `MYSQL_USER`             | `root`      | 数据库用户              |
| `MYSQL_PASSWORD`         | (空)         | 数据库密码              |
| `MYSQL_DATABASE`         | `mysql`     | 数据库名称              |
| `MYSQL_URL`              | (空)         | MySQL 的主要连接 URL    |
| `TLS_MYSQL_DATABASE_URL` | (空)         | 启用 SSL/TLS 的连接 URL |

### PostgreSQL 环境变量

这些环境变量定义 PostgreSQL 连接：

| 环境变量                        | 描述                   |
| --------------------------- | -------------------- |
| `POSTGRES_URL`              | PostgreSQL 的主要连接 URL |
| `DATABASE_URL`              | 备选连接 URL（自动检测）       |
| `PGURL`                     | 备选连接 URL             |
| `PG_URL`                    | 备选连接 URL             |
| `TLS_POSTGRES_DATABASE_URL` | 启用 SSL/TLS 的连接 URL   |
| `TLS_DATABASE_URL`          | 备选启用 SSL/TLS 的连接 URL |

如果未提供连接 URL，Bun 会检查这些单独的参数：

| 环境变量         | 回退变量                       | 默认值         | 描述    |
| ------------ | -------------------------- | ----------- | ----- |
| `PGHOST`     | -                          | `localhost` | 数据库主机 |
| `PGPORT`     | -                          | `5432`      | 数据库端口 |
| `PGUSERNAME` | `PGUSER`、`USER`、`USERNAME` | `postgres`  | 数据库用户 |
| `PGPASSWORD` | -                          | (空)         | 数据库密码 |
| `PGDATABASE` | -                          | username    | 数据库名称 |

### SQLite 环境变量

SQLite 连接可以使用 `DATABASE_URL` 配置，当其包含 SQLite 兼容的 URL 时：

```bash theme={null}
# 这些都会被识别为 SQLite
DATABASE_URL=":memory:"
DATABASE_URL="sqlite://./app.db"
DATABASE_URL="file:///absolute/path/to/db.sqlite"
```

**注意：** PostgreSQL 特定的环境变量（如 `POSTGRES_URL` 和 `PGHOST`）在使用 SQLite 时会被忽略。

***

## 运行时预连接

Bun 可以在启动时预连接到 PostgreSQL，在你的应用程序代码运行之前，这样第一个查询就不需要承受连接延迟。

```bash theme={null}
# 启用 PostgreSQL 预连接
bun --sql-preconnect index.js

# 与 DATABASE_URL 环境变量一起使用
DATABASE_URL=postgres://user:pass@localhost:5432/db bun --sql-preconnect index.js

# 可以与其他运行时标志结合使用
bun --sql-preconnect --hot index.js
```

`--sql-preconnect` 标志在启动时使用你配置的环境变量建立 PostgreSQL 连接。如果连接失败，错误会被处理而不会导致应用程序崩溃。

***

## 连接选项

你可以通过向 `SQL` 构造函数传递选项来手动配置连接。选项因适配器而异：

### MySQL 选项

```ts theme={null}
import { SQL } from "bun";

const sql = new SQL({
  // 使用选项对象时需要为 MySQL 设置
  adapter: "mysql",

  // 连接详情
  hostname: "localhost",
  port: 3306,
  database: "myapp",
  username: "dbuser",
  password: "secretpass",

  // Unix 套接字连接（hostname/port 的替代方案）
  // socket: "/var/run/mysqld/mysqld.sock",

  // 连接池设置
  max: 20, // 池中最大连接数（默认：10）
  idleTimeout: 30, // 30 秒后关闭空闲连接
  maxLifetime: 0, // 连接生存时间（秒）（0 = 永久）
  connectionTimeout: 30, // 建立新连接时的超时时间

  // SSL/TLS 选项
  ssl: "prefer", // 或 "disable"、"require"、"verify-ca"、"verify-full"
  // tls: {
  //   rejectUnauthorized: true,
  //   ca: "path/to/ca.pem",
  //   key: "path/to/key.pem",
  //   cert: "path/to/cert.pem",
  // },

  // 回调
  onconnect: client => {
    console.log("已连接到 MySQL");
  },
  onclose: (client, err) => {
    if (err) {
      console.error("MySQL 连接错误:", err);
    } else {
      console.log("MySQL 连接已关闭");
    }
  },
});
```

### PostgreSQL 选项

```ts theme={null}
import { SQL } from "bun";

const sql = new SQL({
  // 连接详情（适配器会自动检测为 PostgreSQL）
  url: "postgres://user:pass@localhost:5432/dbname",

  // 备选连接参数
  hostname: "localhost",
  port: 5432,
  database: "myapp",
  username: "dbuser",
  password: "secretpass",

  // 连接池设置
  max: 20, // 池中最大连接数
  idleTimeout: 30, // 30 秒后关闭空闲连接
  maxLifetime: 0, // 连接生存时间（秒）（0 = 永久）
  connectionTimeout: 30, // 建立新连接时的超时时间

  // SSL/TLS 选项
  tls: true,
  // tls: {
  //   rejectUnauthorized: true,
  //   requestCert: true,
  //   ca: "path/to/ca.pem",
  //   key: "path/to/key.pem",
  //   cert: "path/to/cert.pem",
  //   checkServerIdentity(hostname, cert) {
  //     ...
  //   },
  // },

  // 回调
  onconnect: client => {
    console.log("已连接到 PostgreSQL");
  },
  onclose: client => {
    console.log("PostgreSQL 连接已关闭");
  },
});
```

### SQLite 选项

```ts theme={null}
import { SQL } from "bun";

const sql = new SQL({
  // 为 SQLite 设置
  adapter: "sqlite",
  filename: "./data/app.db", // 或 ":memory:" 用于内存数据库

  // SQLite 特有的访问模式
  readonly: false, // 以只读模式打开
  create: true, // 如果数据库不存在则创建
  readwrite: true, // 允许读写操作

  // SQLite 数据处理
  strict: true, // 启用严格模式以获得更好的类型安全
  safeIntegers: false, // 对超出 JS 数字范围的整数使用 BigInt

  // 回调
  onconnect: client => {
    console.log("SQLite 数据库已打开");
  },
  onclose: client => {
    console.log("SQLite 数据库已关闭");
  },
});
```

<Accordion title="SQLite 连接说明">
  * **连接池**：SQLite 不使用连接池，因为它是一个基于文件的数据库。每个 `SQL` 实例代表一个单一连接。
  * **事务**：SQLite 通过保存点支持嵌套事务，类似于 PostgreSQL。
  * **并发访问**：SQLite 通过文件锁定处理并发访问。使用 WAL 模式以获得更好的并发性。
  * **内存数据库**：使用 `:memory:` 会创建一个临时数据库，其生命周期仅与连接相同。
</Accordion>

***

## 动态密码

对于备选认证方案（如访问令牌）或具有轮换密码的数据库，将 `password` 设置为同步或异步函数。Bun 在连接时调用它以解析密码。

```ts theme={null}
import { SQL } from "bun";

const sql = new SQL(url, {
  // 其他连接配置
  ...
  // 数据库用户的密码函数
  password: async () => await signer.getAuthToken(),
});
```

***

## SQLite 特有功能

### 查询执行

SQLite 同步执行查询，与使用异步 I/O 的 PostgreSQL 不同。API 仍然返回 Promise：

```ts theme={null}
const sqlite = new SQL("sqlite://app.db");

// 与 PostgreSQL 用法相同，但在底层同步执行
const users = await sqlite`SELECT * FROM users`;

// 参数用法相同
const user = await sqlite`SELECT * FROM users WHERE id = ${userId}`;
```

### SQLite 编译指令

使用 `PRAGMA` 语句配置 SQLite 行为：

```ts theme={null}
const sqlite = new SQL("sqlite://app.db");

// 启用外键
await sqlite`PRAGMA foreign_keys = ON`;

// 将日志模式设置为 WAL 以获得更好的并发性
await sqlite`PRAGMA journal_mode = WAL`;

// 检查完整性
const integrity = await sqlite`PRAGMA integrity_check`;
```

### 数据类型差异

SQLite 的类型系统比 PostgreSQL 更灵活：

```ts theme={null}
// SQLite 将数据存储在 5 种存储类中：NULL、INTEGER、REAL、TEXT、BLOB
const sqlite = new SQL("sqlite://app.db");

// SQLite 对类型的容忍度更高
await sqlite`
  CREATE TABLE flexible (
    id INTEGER PRIMARY KEY,
    data TEXT,        -- 可以以字符串形式存储数字
    value NUMERIC,    -- 可以存储整数、实数或文本
    blob BLOB         -- 二进制数据
  )
`;

// JavaScript 值会自动转换
await sqlite`INSERT INTO flexible VALUES (${1}, ${"text"}, ${123.45}, ${Buffer.from("binary")})`;
```

***

## 事务

要启动新事务，使用 `sql.begin`。此方法同时适用于 PostgreSQL 和 SQLite。对于 PostgreSQL，它会从池中保留一个专用连接。对于 SQLite，它会在单个连接上开始一个事务。

`BEGIN` 命令会自动发送，包括你指定的任何可选配置。如果在事务期间发生错误，Bun 会发出 `ROLLBACK`。

### 基本事务

```ts theme={null}
await sql.begin(async tx => {
  // 此函数中的所有查询都在一个事务中运行
  await tx`INSERT INTO users (name) VALUES (${"Alice"})`;
  await tx`UPDATE accounts SET balance = balance - 100 WHERE user_id = 1`;

  // 如果没有抛出错误，事务会自动提交
  // 如果发生任何错误，则回滚
});
```

要流水线执行事务中的查询，从回调中返回查询数组：

```ts theme={null}
await sql.begin(async tx => {
  return [
    tx`INSERT INTO users (name) VALUES (${"Alice"})`,
    tx`UPDATE accounts SET balance = balance - 100 WHERE user_id = 1`,
  ];
});
```

### 保存点

保存点会在事务内创建中间检查点，这样部分事务可以回滚而不需要中止整个事务。

```ts theme={null}
await sql.begin(async tx => {
  await tx`INSERT INTO users (name) VALUES (${"Alice"})`;

  await tx.savepoint(async sp => {
    // 这部分可以单独回滚
    await sp`UPDATE users SET status = 'active'`;
    if (someCondition) {
      throw new Error("回滚到保存点");
    }
  });

  // 即使保存点已回滚，也可以继续事务
  await tx`INSERT INTO audit_log (action) VALUES ('user_created')`;
});
```

### 分布式事务

两阶段提交 (2PC) 是一种分布式事务协议：阶段 1 中协调器准备每个节点，确保其数据已写入并准备好提交；阶段 2 中节点根据协调器的决定提交或回滚。

在 PostgreSQL 和 MySQL 中，分布式事务会持续存在，超出其原始会话的生命周期，因此特权用户或协调器可以在以后提交或回滚它们。PostgreSQL 将其实现为预备事务；MySQL 使用 XA 事务。

分布式事务期间未捕获的异常会回滚所有更改。否则，你可以在之后提交或回滚事务。

```ts theme={null}
// 开始分布式事务
await sql.beginDistributed("tx1", async tx => {
  await tx`INSERT INTO users (name) VALUES (${"Alice"})`;
});

// 稍后，提交或回滚
await sql.commitDistributed("tx1");
// 或
await sql.rollbackDistributed("tx1");
```

***

## 认证

Bun 支持 SCRAM-SHA-256 (SASL)、MD5 和明文认证。SASL 推荐用于更好的安全性。参见 [Postgres SASL Authentication](https://www.postgresql.org/docs/current/sasl-authentication.html)。

### SSL 模式概述

PostgreSQL 的 SSL/TLS 模式控制是否需要安全连接以及执行多少证书验证。

```ts theme={null}
const sql = new SQL({
  hostname: "localhost",
  username: "user",
  password: "password",
  ssl: "disable", // | "prefer" | "require" | "verify-ca" | "verify-full"
});
```

| SSL 模式        | 描述                                          |
| ------------- | ------------------------------------------- |
| `disable`     | 不使用 SSL/TLS。如果服务器要求 SSL，连接会失败。如果未指定，这是默认模式。 |
| `prefer`      | 首先尝试 SSL，如果 SSL 失败则回退到非 SSL。                |
| `require`     | 需要 SSL 但不验证证书。如果无法建立 SSL，则失败。               |
| `verify-ca`   | 验证服务器证书由受信任的 CA 签名。如果验证失败，则失败。              |
| `verify-full` | 最安全的模式。验证证书和主机名是否匹配。防止不可信证书和 MITM 攻击。       |

### 使用连接字符串

你也可以在连接字符串中设置 SSL 模式：

```ts theme={null}
// 使用 prefer 模式
const sql = new SQL("postgres://user:password@localhost/mydb?sslmode=prefer");

// 使用 verify-full 模式
const sql = new SQL("postgres://user:password@localhost/mydb?sslmode=verify-full");
```

***

## 连接池

Bun 的 SQL 客户端管理一个连接池：数据库连接在查询之间被重用，而不是为每个查询打开和关闭，并且池限制了并发连接的数量。

```ts theme={null}
const sql = new SQL({
  // 池配置
  max: 20, // 最多 20 个并发连接
  idleTimeout: 30, // 30 秒后关闭空闲连接
  maxLifetime: 3600, // 最大连接生存时间 1 小时
  connectionTimeout: 10, // 连接超时 10 秒
});
```

在执行查询之前不会建立连接。

```ts theme={null}
const sql = Bun.SQL(); // 未创建连接

await sql`...`; // 池启动直到达到最大值（如果可能），使用第一个可用连接
await sql`...`; // 之前的连接被重用

// 现在同时使用两个连接
await Promise.all([
  sql`INSERT INTO users ${sql({ name: "Alice" })}`,
  sql`UPDATE users SET name = ${user.name} WHERE id = ${user.id}`,
]);

await sql.close(); // 等待所有查询完成并关闭池中的所有连接
await sql.close({ timeout: 5 }); // 等待 5 秒并关闭池中的所有连接
await sql.close({ timeout: 0 }); // 立即关闭池中的所有连接
```

***

## 保留连接

`sql.reserve()` 从池中取出一个连接，并返回一个包装它的客户端，这样你可以在隔离的连接上运行查询。

```ts theme={null}
// 从池中获取独占连接
const reserved = await sql.reserve();

try {
  await reserved`INSERT INTO users (name) VALUES (${"Alice"})`;
} finally {
  // 重要：将连接释放回池
  reserved.release();
}

// 或使用 Symbol.dispose
{
  using reserved = await sql.reserve();
  await reserved`SELECT 1`;
} // 自动释放
```

***

## 预编译语句

默认情况下，Bun 的 SQL 客户端为其可以推断为静态的查询创建命名预编译语句，这样速度更快。要禁用它，在连接选项中设置 `prepare: false`：

```ts theme={null}
const sql = new SQL({
  // ... 其他选项 ...
  prepare: false, // 禁止在服务器上持久化命名预编译语句
});
```

当 `prepare: false` 设置时：

查询仍然使用"extended"协议，但作为[未命名预编译语句](https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY)运行。未命名的预编译语句只存活到下一个指定未命名语句作为目标的 Parse 语句之前。

* 参数绑定仍然对 SQL 注入是安全的
* 每个查询由服务器从头解析和计划
* 查询不会被[流水线处理](https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-PIPELINING)

你可能希望在以下情况下使用 `prepare: false`：

* 在事务模式下使用 PGBouncer（不过自 PGBouncer 1.21.0 起，协议级别的命名预编译语句在正确配置时得到支持）
* 调试查询执行计划
* 处理需要频繁重新生成查询计划的动态 SQL
* 每个查询只支持一个命令（除非你使用 `sql``.simple()`）

禁用预编译语句可能会减慢频繁使用不同参数运行的查询，因为服务器会从头解析和计划每个查询。

***

## 错误处理

客户端为不同的失败场景提供类型化错误。错误是数据库特定的，并扩展了基错误类：

### 错误类

```ts theme={null}
import { SQL } from "bun";

try {
  await sql`SELECT * FROM users`;
} catch (error) {
  if (error instanceof SQL.PostgresError) {
    // PostgreSQL 特定错误
    console.log(error.code); // PostgreSQL 错误码
    console.log(error.detail); // 详细错误消息
    console.log(error.hint); // PostgreSQL 提供的提示
  } else if (error instanceof SQL.SQLiteError) {
    // SQLite 特定错误
    console.log(error.code); // SQLite 错误码（如 "SQLITE_CONSTRAINT"）
    console.log(error.errno); // SQLite 错误号
    console.log(error.byteOffset); // SQL 语句中的字节偏移（如果可用）
  } else if (error instanceof SQL.SQLError) {
    // 通用 SQL 错误（基类）
    console.log(error.message);
  }
}
```

<Accordion title="PostgreSQL 特定错误码">
  ### PostgreSQL 连接错误

  | 连接错误                              | 描述                                                                                                                         |
  | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
  | `ERR_POSTGRES_CONNECTION_CLOSED`  | 已建立的连接被终止                                                                                                                  |
  | `ERR_POSTGRES_CONNECTION_FAILED`  | 连接被接受但在握手完成前关闭（例如服务器仍在启动）。在查询等待时以退避方式重试，直到 `connectionTimeout`。注意：服务器在启动期间发送的错误（如 `57P03`）会表现为 `ERR_POSTGRES_SERVER_ERROR` |
  | `ERR_POSTGRES_CONNECTION_REFUSED` | 连接被拒绝，因为地址上没有进程在监听。立即失败，不重试                                                                                                |
  | `ERR_POSTGRES_CONNECTION_TIMEOUT` | 在超时时间内未能建立连接                                                                                                               |
  | `ERR_POSTGRES_IDLE_TIMEOUT`       | 连接因不活动而关闭                                                                                                                  |
  | `ERR_POSTGRES_LIFETIME_TIMEOUT`   | 连接超过最大生存时间                                                                                                                 |
  | `ERR_POSTGRES_TLS_NOT_AVAILABLE`  | SSL/TLS 连接不可用                                                                                                              |
  | `ERR_POSTGRES_TLS_UPGRADE_FAILED` | 无法将连接升级到 SSL/TLS                                                                                                           |

  ### 认证错误

  | 认证错误                                             | 描述            |
  | ------------------------------------------------ | ------------- |
  | `ERR_POSTGRES_AUTHENTICATION_FAILED_PBKDF2`      | 密码认证失败        |
  | `ERR_POSTGRES_UNKNOWN_AUTHENTICATION_METHOD`     | 服务器请求未知的认证方法  |
  | `ERR_POSTGRES_UNSUPPORTED_AUTHENTICATION_METHOD` | 服务器请求不支持的认证方法 |
  | `ERR_POSTGRES_INVALID_SERVER_KEY`                | 认证期间服务器密钥无效   |
  | `ERR_POSTGRES_INVALID_SERVER_SIGNATURE`          | 服务器签名无效       |
  | `ERR_POSTGRES_SASL_SIGNATURE_INVALID_BASE64`     | SASL 签名编码无效   |
  | `ERR_POSTGRES_SASL_SIGNATURE_MISMATCH`           | SASL 签名验证失败   |

  ### 查询错误

  | 查询错误                                 | 描述                          |
  | ------------------------------------ | --------------------------- |
  | `ERR_POSTGRES_SYNTAX_ERROR`          | SQL 语法无效（扩展了 `SyntaxError`） |
  | `ERR_POSTGRES_SERVER_ERROR`          | PostgreSQL 服务器的通用错误         |
  | `ERR_POSTGRES_INVALID_QUERY_BINDING` | 参数绑定无效                      |
  | `ERR_POSTGRES_QUERY_CANCELLED`       | 查询被取消                       |
  | `ERR_POSTGRES_NOT_TAGGED_CALL`       | 查询在未使用标签调用时被调用              |

  ### 数据类型错误

  | 数据类型错误                                                  | 描述              |
  | ------------------------------------------------------- | --------------- |
  | `ERR_POSTGRES_INVALID_BINARY_DATA`                      | 无效的二进制数据格式      |
  | `ERR_POSTGRES_INVALID_BYTE_SEQUENCE`                    | 无效的字节序列         |
  | `ERR_POSTGRES_INVALID_BYTE_SEQUENCE_FOR_ENCODING`       | 编码错误            |
  | `ERR_POSTGRES_INVALID_CHARACTER`                        | 数据中包含无效字符       |
  | `ERR_POSTGRES_OVERFLOW`                                 | 数值溢出            |
  | `ERR_POSTGRES_UNSUPPORTED_BYTEA_FORMAT`                 | 不支持的二进制格式       |
  | `ERR_POSTGRES_UNSUPPORTED_INTEGER_SIZE`                 | 不支持的整数大小        |
  | `ERR_POSTGRES_MULTIDIMENSIONAL_ARRAY_NOT_SUPPORTED_YET` | 尚不支持多维数组        |
  | `ERR_POSTGRES_NULLS_IN_ARRAY_NOT_SUPPORTED_YET`         | 尚不支持数组中的 NULL 值 |

  ### 协议错误

  | 协议错误                                    | 描述        |
  | --------------------------------------- | --------- |
  | `ERR_POSTGRES_EXPECTED_REQUEST`         | 期望客户端请求   |
  | `ERR_POSTGRES_EXPECTED_STATEMENT`       | 期望预编译语句   |
  | `ERR_POSTGRES_INVALID_BACKEND_KEY_DATA` | 无效的后端密钥数据 |
  | `ERR_POSTGRES_INVALID_MESSAGE`          | 无效的协议消息   |
  | `ERR_POSTGRES_INVALID_MESSAGE_LENGTH`   | 无效的消息长度   |
  | `ERR_POSTGRES_UNEXPECTED_MESSAGE`       | 意外的消息类型   |

  ### 事务错误

  | 事务错误                                     | 描述          |
  | ---------------------------------------- | ----------- |
  | `ERR_POSTGRES_UNSAFE_TRANSACTION`        | 检测到不安全的事务操作 |
  | `ERR_POSTGRES_INVALID_TRANSACTION_STATE` | 无效的事务状态     |
</Accordion>

### SQLite 特定错误

SQLite 错误携带 SQLite 的标准错误码和号：

<Accordion title="常见 SQLite 错误码">
  | 错误码                 | errno | 描述                            |
  | ------------------- | ----- | ----------------------------- |
  | `SQLITE_CONSTRAINT` | 19    | 约束违反（UNIQUE、CHECK、NOT NULL 等） |
  | `SQLITE_BUSY`       | 5     | 数据库已锁定                        |
  | `SQLITE_LOCKED`     | 6     | 数据库中的表已锁定                     |
  | `SQLITE_READONLY`   | 8     | 尝试写入只读数据库                     |
  | `SQLITE_IOERR`      | 10    | 磁盘 I/O 错误                     |
  | `SQLITE_CORRUPT`    | 11    | 数据库磁盘映像已损坏                    |
  | `SQLITE_FULL`       | 13    | 数据库或磁盘已满                      |
  | `SQLITE_CANTOPEN`   | 14    | 无法打开数据库文件                     |
  | `SQLITE_PROTOCOL`   | 15    | 数据库锁协议错误                      |
  | `SQLITE_SCHEMA`     | 17    | 数据库模式已更改                      |
  | `SQLITE_TOOBIG`     | 18    | 字符串或 BLOB 超过大小限制              |
  | `SQLITE_MISMATCH`   | 20    | 数据类型不匹配                       |
  | `SQLITE_MISUSE`     | 21    | 库使用不正确                        |
  | `SQLITE_AUTH`       | 23    | 授权被拒绝                         |

  错误处理示例：

  ```ts theme={null}
  const sqlite = new SQL("sqlite://app.db");

  try {
    await sqlite`INSERT INTO users (id, name) VALUES (1, 'Alice')`;
    await sqlite`INSERT INTO users (id, name) VALUES (1, 'Bob')`; // 重复 ID
  } catch (error) {
    if (error instanceof SQL.SQLiteError) {
      if (error.code === "SQLITE_CONSTRAINT") {
        console.log("约束违反:", error.message);
        // 处理唯一约束违反
      }
    }
  }
  ```
</Accordion>

***

## 数字和 BigInt

超出 53 位整数范围的数字会作为字符串返回：

```ts theme={null}
import { sql } from "bun";

const [{ x, y }] = await sql`SELECT 9223372036854777 as x, 12345 as y`;

console.log(typeof x, x); // "string" "9223372036854777"
console.log(typeof y, y); // "number" 12345
```

***

## 使用 BigInt 替代字符串

要将大数字作为 `BigInt` 而不是字符串获取，在创建 SQL 客户端时将 `bigint` 选项设置为 `true`：

```ts theme={null}
const sql = new SQL({
  bigint: true,
});

const [{ x }] = await sql`SELECT 9223372036854777 as x`;

console.log(typeof x, x); // "bigint" 9223372036854777n
```

***

## 路线图

我们尚未完成的事项：

* 使用 `--db-preconnect` Bun CLI 标志进行连接预加载
* 列名转换（例如，`snake_case` 到 `camelCase`）。这主要受限于在 C++ 中使用 WebKit 的 `WTF::String` 实现大小写转换的 Unicode 感知实现。
* 列类型转换

***

## 数据库特有功能

#### 认证方法

MySQL 支持多种认证插件，会自动协商：

* **`mysql_native_password`** - 传统 MySQL 认证，广泛兼容
* **`caching_sha2_password`** - MySQL 8.0+ 默认，使用 RSA 密钥交换更安全
* **`sha256_password`** - 基于 SHA-256 的认证

客户端会自动处理服务器请求时的认证插件切换，包括在非 SSL 连接上进行安全的密码交换。

#### 预编译语句与性能

MySQL 对所有参数化查询使用服务器端预编译语句：

```ts theme={null}
// 这会在服务器上自动创建预编译语句
const user = await mysql`SELECT * FROM users WHERE id = ${userId}`;

// 预编译语句会被缓存并为相同查询重用
for (const id of userIds) {
  // 相同的预编译语句被重用
  await mysql`SELECT * FROM users WHERE id = ${id}`;
}

// 查询流水线 - 无需等待即可发送多个语句
const [users, orders, products] = await Promise.all([
  mysql`SELECT * FROM users WHERE active = ${true}`,
  mysql`SELECT * FROM orders WHERE status = ${"pending"}`,
  mysql`SELECT * FROM products WHERE in_stock = ${true}`,
]);
```

#### 多结果集

MySQL 可以从多语句查询返回多个结果集：

```ts theme={null}
const mysql = new SQL("mysql://user:pass@localhost/mydb");

// 使用 simple() 方法的多语句查询
const multiResults = await mysql`
  SELECT * FROM users WHERE id = 1;
  SELECT * FROM orders WHERE user_id = 1;
`.simple();
```

#### 字符集与排序规则

`Bun.SQL` 对 MySQL 连接使用 `utf8mb4` 字符集，覆盖所有 Unicode，包括 emoji。

#### 连接属性

Bun 发送客户端信息到 MySQL 用于监控：

```ts theme={null}
// 这些属性会自动发送：
// _client_name: "Bun"
// _client_version: <bun version>
// 你可以在 MySQL 的 performance_schema.session_connect_attrs 中看到它们
```

#### 类型处理

MySQL 类型被转换为 JavaScript 类型：

| MySQL 类型                                | JavaScript 类型           | 说明                                                          |
| --------------------------------------- | ----------------------- | ----------------------------------------------------------- |
| INT, TINYINT, MEDIUMINT                 | number                  | 在安全整数范围内                                                    |
| BIGINT                                  | string, number 或 BigInt | 如果值适合 i32/u32 则为 number，否则为 string 或 BigInt，取决于 `bigint` 选项 |
| DECIMAL, NUMERIC                        | string                  | 以保持精度                                                       |
| FLOAT, DOUBLE                           | number                  |                                                             |
| DATE                                    | Date                    | JavaScript Date 对象                                          |
| DATETIME, TIMESTAMP                     | Date                    | 解码为 UTC（见下面的说明）；`0000-00-00` 变为无效日期                         |
| TIME                                    | number                  | 微秒总数                                                        |
| YEAR                                    | number                  |                                                             |
| CHAR, VARCHAR, VARSTRING, STRING        | string                  |                                                             |
| TINY TEXT, MEDIUM TEXT, TEXT, LONG TEXT | string                  |                                                             |
| TINY BLOB, MEDIUM BLOB, BLOB, LONG BLOB | string                  | BLOB 类型是 TEXT 类型的别名                                         |
| JSON                                    | object/array            | 自动解析                                                        |
| BIT(1)                                  | boolean                 | MySQL 中的 BIT(1)                                             |
| GEOMETRY                                | string                  | 几何数据                                                        |

`DATETIME` 和 `TIMESTAMP` 值在线上没有时区，因此 Bun 将其读取为 **UTC**——你获得的 `Date` 具有与存储时相同的 UTC 挂钟时间，无论机器的时区如何。这与值的写入方式匹配（绑定的 `Date` 存储其 UTC 组件）。这也适用于 PostgreSQL 的 `timestamp`（不带时区）；`timestamptz` 带有显式偏移，不受影响。

#### 与 PostgreSQL 的差异

API 是统一的，但行为有所不同：

1. **参数占位符**：MySQL 内部使用 `?`，但 Bun 会自动转换 `$1, $2` 风格
2. **RETURNING 子句**：MySQL 不支持 RETURNING；使用 `result.lastInsertRowid` 或单独的 SELECT
3. **数组类型**：MySQL 没有像 PostgreSQL 那样的原生数组类型

### MySQL 特有功能

我们尚未实现 `LOAD DATA INFILE` 支持。

### PostgreSQL 特有功能

我们尚未实现：

* `COPY` 支持
* `LISTEN` 支持
* `NOTIFY` 支持

我们也没有实现一些较不常见的功能，如：

* GSSAPI 认证
* `SCRAM-SHA-256-PLUS` 支持
* Point 和 PostGIS 类型
* 所有多维整数数组类型（仅支持少数几种类型）

***

## 常见模式与最佳实践

### 处理 MySQL 结果集

```ts theme={null}
// 获取 INSERT 后的 ID
const result = await mysql`INSERT INTO users (name) VALUES (${"Alice"})`;
console.log(result.lastInsertRowid); // MySQL 的 LAST_INSERT_ID()

// 处理受影响的行数
const updated = await mysql`UPDATE users SET active = ${false} WHERE age < ${18}`;
console.log(updated.affectedRows); // 已更新的行数

// 使用 MySQL 特有函数
const now = await mysql`SELECT NOW() as current_time`;
const uuid = await mysql`SELECT UUID() as id`;
```

### MySQL 错误处理

```ts theme={null}
try {
  await mysql`INSERT INTO users (email) VALUES (${"duplicate@email.com"})`;
} catch (error) {
  if (error.code === "ER_DUP_ENTRY") {
    console.log("检测到重复条目");
  } else if (error.code === "ER_ACCESS_DENIED_ERROR") {
    console.log("访问被拒绝");
  } else if (error.code === "ER_BAD_DB_ERROR") {
    console.log("数据库不存在");
  }
  // MySQL 错误码与 mysql/mysql2 包兼容
}
```

### MySQL 性能建议

1. **使用连接池**：根据工作负载设置适当的 `max` 池大小
2. **启用预编译语句**：默认启用，可以提升性能
3. **为批量操作使用事务**：在事务中组合相关查询
4. **正确使用索引**：MySQL 严重依赖索引来提升查询性能
5. **使用 `utf8mb4` 字符集**：默认设置，处理所有 Unicode 字符

***

## 常见问题

<AccordionGroup>
  <Accordion title="为什么是 `Bun.sql` 而不是 `Bun.postgres`？">
    计划是添加更多数据库驱动程序。现在统一的 API 支持 PostgreSQL、MySQL 和 SQLite。
  </Accordion>

  <Accordion title="如何知道正在使用哪个数据库适配器？">
    适配器会自动从连接字符串检测：

    * 以 `mysql://` 或 `mysql2://` 开头的 URL 使用 MySQL
    * 匹配 SQLite 模式（`:memory:`、`sqlite://`、`file://`）的 URL 使用 SQLite
    * 其他所有内容默认为 PostgreSQL
  </Accordion>

  <Accordion title="MySQL 存储过程是否受支持？">
    是的，支持存储过程，包括 OUT 参数和多结果集：

    ```ts theme={null}
    // 调用存储过程
    const results = await mysql`CALL GetUserStats(${userId}, @total_orders)`;

    // 获取 OUT 参数
    const outParam = await mysql`SELECT @total_orders as total`;
    ```
  </Accordion>

  <Accordion title="可以使用 MySQL 特有的 SQL 语法吗？">
    是的，你可以使用任何 MySQL 特有的语法：

    ```ts theme={null}
    // MySQL 特有语法正常工作
    await mysql`SET @user_id = ${userId}`;
    await mysql`SHOW TABLES`;
    await mysql`DESCRIBE users`;
    await mysql`EXPLAIN SELECT * FROM users WHERE id = ${id}`;
    ```
  </Accordion>
</AccordionGroup>

***

## 为什么不直接使用现有库？

你也可以在 Bun 中使用 npm 包，如 postgres.js、pg 和 node-postgres。它们都是很好的选择。

有两个原因：

1. 我们认为在 Bun 中内置数据库驱动对开发者来说更简单。你花在挑选库上的时间，本可以用来构建你的应用。
2. 我们使用一些 JavaScriptCore 引擎内部机制来更快地创建对象，这在库中很难实现。

## 致谢

衷心感谢 [@porsager](https://github.com/porsager) 的 [postgres.js](https://github.com/porsager/postgres) 为 API 接口提供了灵感。
