# @pureprofile/pg-schema-dump 中文文档

规范地址：https://npmcn.com/package/%40pureprofile/pg-schema-dump/

版本：2\.1\.2
npm 元数据更新时间：2026-11-02T06:04:54.809Z
中文译文更新时间：2026-11-02T06:18:20.931Z
软件包许可：MIT

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40pureprofile/pg-schema-dump
原文对照：https://npmcn.com/package/%40pureprofile/pg-schema-dump/?lang=original
项目主页：https://github.com/pureprofile/pg-schema-dump#readme
源码仓库：https://github.com/pureprofile/pg-schema-dump

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：122 kB，41 个文件
- 模块格式：CommonJS
- TypeScript 类型：包内自带
- 运行依赖：5 个
- 首次发布：2020-07-15T08:36:43.219Z
- 最新版本发布：2026-11-02T06:04:54.470Z
- 已发布版本数：9（近 12 个月 4 个）

## 中文简介

`@pureprofile/pg-schema-dump` 将PostgreSQL模式转储为许多小的、单独命名的模式 `.sql` 文件，因此模式变成 **文本可比较和可变**.

## 安装

```sh
npm install @pureprofile/pg-schema-dump
```

也可以使用：`pnpm add @pureprofile/pg-schema-dump`、`yarn add @pureprofile/pg-schema-dump`、`bun add @pureprofile/pg-schema-dump`

## README

# pg-schema-dump

`@pureprofile/pg-schema-dump` 将PostgreSQL模式转储为许多小的、单独命名的模式 `.sql` 文件，因此模式变成 **文本可比较和可变**.

每个对象都被写入一个具有确定名称和规范化SQL的文件，而不是单个转储。然后可以将两个数据库与一个数据库进行比较 `diff` 或者提交git来跟踪模式漂移。转储也可以是 **恢复** 到空数据库中，这使得已提交的转储可用作测试fixture。

写入属于单个表的对象——它的索引、触发器、主键、唯一和检查约束，以及它的序列的所有权 **到那个表的文件中**，因为它们都不能从其他地方引用。外键在每个表中获取一个文件，因为它们必须等到每个表都存在。

## 为什么

`pg_dump` 输出的排序和格式化方式使得区分两个模式非常痛苦。这个工具产生 **相同模式的相同字节输出**：收集器在SQL中对行排序，列排序是确定性的，SQL是规范化的。结果是一个目录，您可以 `diff -r`，签入git，或者反馈到一个空的数据库。

## 安装

全球CLI:

```bash
npm install -g @pureprofile/pg-schema-dump
```

作为图书馆：

```bash
npm install @pureprofile/pg-schema-dump
# or: pnpm add @pureprofile/pg-schema-dump
```

## CLI使用

```bash
pg-schema-dump --url <connection-string> [--out <dir>] [scope options]
```

| 选项 | 要求 | 描述 |
| --- | --- | --- |
| `--url` | 是 | PostgreSQL要转储的数据库连接字符串。 |
| `--out` | 否 | 要将转储写入的目录。请参阅下面的默认值。 |
| `--scope-file` | 否 | JSON清单的路径，其中列出了要包含的内容。看到 **范围**. |
| `--include-schema` | 否 | 包括一个完整的模式。可重复的。 |
| `--include-table` | 否 | 包括一个 `schema.table`． 可重复的。 |
| `--include-function` | 否 | 包括一个 `schema.function` 明确。可重复的。 |

当 `--out` ，则输出为 `pg-schema-dump/<NODE_ENV>/<dbName>` ( `NODE_ENV` 默认为 `development` 和 `dbName` 从连接中读取)。

如果没有作用域选项，整个数据库将被转储。

### 示例

将数据库转储到一个显式目录中：

```bash
pg-schema-dump \
  --url postgres://user:pass@db.example.com:5432/mydb \
  --out ./schema/mydb
```

使用默认输出位置转储(`./pg-schema-dump/development/mydb`):

```bash
pg-schema-dump --url postgres://user:pass@localhost/mydb
```

比较两个数据库的模式：

```bash
pg-schema-dump --url postgres://user:pass@host-a/app --out ./a
pg-schema-dump --url postgres://user:pass@host-b/app --out ./b
diff -r ./a ./b
```

## 范围

转储整个遗留数据库可能会产生数千个文件，其中大多数都无关紧要。作用域将转储缩小到您真正关心的表。预期的工作流是 **提交清单** 你故意编辑：

```json
{
  "schemas": ["billing", "archive"],
  "tables": ["public.orders", "public.customers"],
  "functions": []
}
```

```bash
pg-schema-dump --url "$DB_URL" --out ./db/schema --scope-file ./db/table-scope.json
```

作用域是一个列表，而不是起点——它是 **不** 通过遵循外键展开，以便在源数据库更改时保持转储的可再现性。计算出一次需要的闭包（递归查询） `pg_constraint.confrelid` 它)，提交结果，并在有新的东西需要一个表时扩展它。

什么作用域拉在你命名的表旁边：

-   **序列** 由范围内表拥有，由其列默认值之一调用，或由 `nextval()` 在作用域内函数的函数体中。第三条路径不是可选的：仅从触发器主体到达的序列对目录的依赖关系图是不可见的，忽略它会导致触发触发器的第一次插入失败，而不是恢复失败。
-   **功能** 可以从范围内的表中访问——列默认值、CHECK约束、表达式索引、触发器和重写规则主体——然后递归地访问这些函数调用的函数。加上任何东西 `functions`． 不是模式中的每个函数，这在大型数据库上是一百个文件和几百个文件的区别。
-   **索引、触发器和约束** 作用域内表的。
-   **的观点** 只有当他们读到的所有东西都在范围关系中 _和_ 函数，因为调用丢失的函数的视图无法创建，就像读取丢失的表一样。一个视图纯粹是由它的依赖关系来判断的，所以一个视图存在于一个你从未命名过的模式中，如果它读取的所有内容都在作用域中，它仍然会被丢弃。
-   **模式** 对于包含的所有内容，因此还原有地方可以放置它。

目标表超出作用域的外键为 **省略而不是发出**，因为指向转储不包含的表的键无法恢复。每一个遗漏都被记录下来：

```
scope: dropped FK public.orders.pricing_rule_id -> pricing.rule (target out of scope)
scope: excluded view public.order_summary (depends on out-of-scope public.shipment)
```

当你扩大视野时，阅读日志——它会告诉你你还错过了什么。

扩展所拥有的对象总是被排除在外，因为 `CREATE EXTENSION` 重新创建它们。

## 编程式的使用

这个包导出一个类， `PgClient`.

```ts
import { PgClient } from '@pureprofile/pg-schema-dump';

// from a connection string...
const client = new PgClient('postgres://user:pass@localhost:5432/mydb');

// ...or from a pg.ClientConfig
const client2 = new PgClient({
  host: 'localhost',
  port: 5432,
  user: 'user',
  password: 'pass',
  database: 'mydb',
});

await client.dumpSchema({ out: './schema/mydb' });
```

连接 **不** 保持开放：每个 `query()` 连接、运行和断开连接。 `dumpSchema` 是异常—它打开一个连接，并行运行所有收集器，然后关闭它。

### 构造函数的选择

```ts
new PgClient(config, {
  logger, // a console-like logger, or `null` to silence all output
  skipSchemas, // extra schemas to skip (pg_catalog & information_schema are always skipped)
  skipFunctions, // function names to skip
  skipExtensions, // extension names to skip, e.g. one unavailable in your target image
  scope, // { includeSchemas, includeTables, includeFunctions } — see Scoping
});
```

### 多么糟糕的报告

`dumpSchema` 解析为 `DumpOmissions` 描述了垃圾场留下的东西。

```ts
import { PgClient } from '@pureprofile/pg-schema-dump';
import type { DumpOmissions } from '@pureprofile/pg-schema-dump';

const client = new PgClient('postgres://user:pass@localhost/mydb', {
  scope: { includeTables: ['public.orders'] },
});

const omissions: DumpOmissions = await client.dumpSchema({ out: './schema/mydb' });

for (const fk of omissions.droppedForeignKeys) {
  // { schema, table, name, target } — `target` is the out-of-scope 'schema.table' it pointed at
  console.warn(`dropped ${fk.schema}.${fk.table}.${fk.name} -> ${fk.target}`);
}
for (const view of omissions.excludedViews) {
  // { view, cause } — `cause` is a 'schema.name' the view reads that the dump does not contain
  console.warn(`excluded ${view.view} (needs ${view.cause})`);
}
```

阅读这些文章时需要注意两点：

-   **`cause` 是缺少依赖，不一定是问题的根源。** 不包括一个 视图排除在它上面构建的任何东西，以这种方式丢弃的视图报告被排除的东西 视图，它读取-所以跟随一个链可能需要多个步骤。
-   **`droppedForeignKeys` 对于未作用域转储始终为空**,但 `excludedViews` 不必是： 中读取关系的视图 `skipSchemas` 基于相同的理由，模式被排除在外，范围或否 范围。还要注意的是，警告日志仅在作用域处于活动状态时才写入，因此是无作用域转储 在这里，没有其他地方报道过这样的观点。

这些信息会被返回并被记录下来。 `logger: null` 是否支持选项和转储 悄悄丢弃外键或视图是后来发现成本最高的失败 因此，调用者在进行自己的报告时会得到程序化的记录，而不必刮去日志。

### 恢复转储

`restoreSchema` 读取转储目录并将其重放到连接的数据库中。它打开了自己的 无论是成功还是失败，都是如此，所以它不需要周围的环境 `connect()` / `end()`：

```ts
const client = new PgClient('postgres://user:pass@localhost/fresh_db');
await client.restoreSchema({ src: './schema/mydb' });
```

文件的应用顺序是每个依赖项都被先前的文件满足，所以转储在一次传递中重播：

| 订单 | 前缀 | 笔记 |
| --- | --- | --- |
| 1 | `extension.` |  |
| 2 | `schema.` |  |
| 3 | `type.` |  |
| 4 | `sequence.` | 所以一张桌子 `default nextval(...)` 解决了 |
| 5 | `function.` | 应用与 `check_function_bodies` 从 |
| 6 | `table.` | 列、约束、序列所有权、索引和触发器 |
| 7 | `fk.` | 在每个表存在之后 |
| 8 | `view.` | 可以引用任何表或函数吗 |

两个结果值得了解：

-   **函数在表之前恢复**下, `check_function_bodies = off`，所以函数体可以引用一个还不存在的表。这就是为什么可以安全地将触发器和表达式索引合并到表文件中。
-   **在添加任何外键之前创建每个表**，所以是外键 _周期_ 表间恢复无需特殊处理。

失败的文件不是致命的：它会转到队列的后面，然后再尝试一次，只要队列来了就再试一次——对于链式视图来说足够了，其中一个视图从另一个尚未创建的视图中进行选择。只有当队列的整个循环完成且没有应用任何内容时，恢复才会放弃，因为此时数据库没有改变，而另一个循环将重复相同的失败。错误名称 **每一个** 文件因自身原因未应用：

```
restoreSchema: 2 file(s) could not be applied:
  - table.public.thing.sql: relation "some_seq" does not exist
  - view.public.other.sql: column reference "x" is ambiguous
```

### 数据库生命周期助手

脚本测试数据库或恢复目标时有用：

```ts
await client.ensureEmptyDb('mydb_test'); // drop if exists, create, switch to it
await client.databaseExists('mydb_test');
await client.createDatabase('mydb_test');
await client.dropDatabase('mydb_test');
await client.switchDatabase('other_db');
await client.truncateTables('mydb_test'); // switch to it, then TRUNCATE ... CASCADE every non-skipped table
```

注意 `truncateTables` 将客户端切换到 `db` 先把它留在那里，同样的方法 `ensureEmptyDb` 它不是对其他数据库的只读操作。

## 输出结构

名称冲突通过追加来消除重复 `.v2`， `.v3`,……

| 前缀 | 对象 | 例子 |
| --- | --- | --- |
| `extension.` | 扩展 | `extension.uuid-ossp.sql` |
| `schema.` | 模式 | `schema.public.sql` |
| `type.` | 枚举类型 | `type.public.mood.sql` |
| `sequence.` | 序列 | `sequence.public.users_id_seq.sql` |
| `function.` | 功能 | `function.public.my_func.sql` |
| `table.` | 一张桌子和它自己的部件 | `table.public.users.sql` |
| `fk.` | 一个表的外键 | `fk.public.orders.sql` |
| `view.` | 的观点 | `view.public.active_users.sql` |

一、 `table.*.sql` 文件保存了 `CREATE TABLE` 列加上命名主键、唯一、检查和排除约束 `ALTER SEQUENCE ... OWNED BY` 对于它拥有的任何序列，然后是它的索引，然后是它的触发器。它作为一个多语句脚本重播。

约束定义来自 `pg_get_constraintdef`，因此组合主键、多列外键和检查约束都是精确复制的。

`nextval(...)` 列默认值逐字发出，而不是折叠为 `serial`/`bigserial`． 这种简写使得Postgres自动创建与转储的序列相冲突的第二个序列，并且它只匹配搜索路径中的表，因为序列名称在其他地方是模式限定的。

## 限制

在采用该工具之前值得阅读—使用以下任何一种方式的模式都将转储或恢复 不完全。

当前未收集：物化视图、分区表(`relkind = 'p'`)， `GENERATED ... AS IDENTITY` 列、域/组合/范围类型（仅限枚举）和行数据—转储仅限模式，因此fixture需要自己的种子脚本。

触发器仅针对普通表收集，因为触发器被写入其表的文件中，而其他表没有。所以一个 `INSTEAD OF` 视图上的触发器是 **不** 扔掉了。它是被故意跳过的，而不是被收集起来然后扔到地板上，但是依赖于它的模式将在没有它的情况下恢复。

**不处理包含文字点的标识符。** 匹配一个限定名，并以键值为 `schema || '.' || name` -在作用域谓词中，在清单项中，以及在将索引，触发器和约束附加到其表的内部分组中。只有当标识符本身不包含点时，这是明确的，这是Postgres允许的。`CREATE SCHEMA "a.b"`）. 模式 `a.b` 表格 `c` 和模式 `a` 表格 `b.c` 对于所有表来说都是不可区分的，因此这样的模式可能会混淆作用域或将对象附加到错误的表。清单验证拒绝包含多个点的条目，这样可以避免常见的错别字，但这不是一个修复方法。发出的DDL不受影响——每个标识符在发出时都被独立引用。

一个恢复顺序形状不能通过桶的等级来解决，它将失败而不是无声地出错 **签名** 接受表的复合行类型(`CREATE FUNCTION f(public.t)`)，其中同一表还携带CHECK约束或表达式索引调用 `f`． 函数首先恢复，和 `check_function_bodies = off` 只放松体验证—签名仍然需要行类型，所以 `f` 之前无法创建 `t`． 但 `t`的文件携带依赖索引，该索引以前不能创建 `f`表文件是单个的多语句查询，它会回滚整个表文件。谁也不能先走。打破它意味着将依赖对象从表文件中分离出来，这牺牲了合并所带来的文件计数减少，因此在真正的模式需要它之前，它一直没有得到解决。恢复停止并命名这两个文件。

## 开发

本项目使用 **规划** 并要求 **节点24** (见 [.nvmrc](https://npmcn.com/package/%40pureprofile/pg-schema-dump/.nvmrc)）。

```bash
pnpm install
pnpm build          # rimraf ./dist && tsc
pnpm check          # ultracite (oxlint + oxfmt) — lint/format check
pnpm fix            # ultracite — apply lint/format fixes
pnpm test:unit      # unit project only (no database required)
pnpm test:e2e       # e2e project only (requires Docker)
pnpm test           # both projects
pnpm test:coverage  # both projects, with coverage over ./src
```

### 测试

-   **单位** 测试(`tests/unit/`)是纯的，不需要数据库。
-   **E2E** 测试(`tests/e2e/`)使用 [Testcontainers](https://testcontainers.com/) 旋转一个短暂的 `postgres:16-alpine` 容器,所以 **Docker必须正在运行** 本地。在CI, `ubuntu-latest` 船舶Docker，所以不需要额外的设置。要对现有数据库而不是容器运行e2e，请设置 `TEST_DB_HOST` (可选 `TEST_DB_PORT` / `TEST_DB_USER` / `TEST_DB_PASSWORD`）。

为整个e2e项目启动一个容器，并且项目运行单分叉和不分叉 文件并行性，所以e2e测试共享一个PostgreSQL实例，而不是每个测试获得一个。

**覆盖范围是封闭的。** [vitest.config.ts](https://npmcn.com/package/%40pureprofile/pg-schema-dump/vitest.config.ts) 对行设置90%的阈值， 语句和函数，以及CI运行 `pnpm test:coverage`，因此，如果低于它，则构建失败。 阈值是基于聚合，而不是每个文件(`perFile: false`, `src/bin.ts` 排除和 没有分支阈值)，所以一个小的未经测试的加法仍然可以传递总数——将90%作为 它是地板，不作为标准的变化。在推送之前在本地运行它。

### Git钩子

[lefthook](https://github.com/evilmartians/lefthook) 是通过 `prepare` 脚本并运行：

-   **未雨绸缪** - - - - - - `pnpm fix` （ultracite）在分级文件上，重新分级修复。
-   **pre-push** - - - - - - `pnpm build`.

CI运行install， lint (`pnpm check`)，构建，以及覆盖的完整测试套件，按此顺序，对每个推送和拉取请求 `main`.

### 释放

发布是自动化的 [release-please](https://github.com/googleapis/release-please)：合并到 `main` 他写道：“打开一个发布PR，并将该PR合并到版本中 `CHANGELOG.md`、标签 `vX.Y.Z`，创建GitHub Release并发布到npm。

因为版本是从提交消息派生的， **每次提交都必须是 [传统的提交](https://www.conventionalcommits.org/en/v1.0.0/)** -不符合要求的信息永远不会触发释放。合并是唯一的合并方式，合并的主体是PR标题，所以在实践中 **公关标题** 重要的是；一个 `PR Title` Check在每个pull请求上验证它。

看到 [文档/ release-please.md](https://npmcn.com/package/%40pureprofile/pg-schema-dump/docs/release-please.md) 对于整个过程，类型到碰撞表，以及故障排除。

## 常见问题

### @pureprofile/pg-schema-dump 是什么？

@pureprofile/pg-schema-dump 将PostgreSQL模式转储为许多小的、单独命名的模式 \.sql 文件，因此模式变成 文本可比较和可变\.

### 如何安装 @pureprofile/pg-schema-dump？

在项目目录运行 npm install @pureprofile/pg-schema-dump，或使用 pnpm add / yarn add / bun add。它还提供命令行工具 pg-schema-dump，可以用 npx pg-schema-dump 直接运行。

### @pureprofile/pg-schema-dump 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @pureprofile/pg-schema-dump 支持 ESM 还是 CommonJS？

以 CommonJS 形式发布，可以直接 require\(\)；在 ES Module 项目中也可以用 import 引入（Node\.js 会把 module\.exports 作为默认导出）。

### @pureprofile/pg-schema-dump 可以商用吗？

@pureprofile/pg-schema-dump 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @pureprofile/pg-schema-dump 还在维护吗？

最新版本 2\.1\.2 发布于 2026/11/02（1 天前）；至今共发布 9 个版本，其中过去 12 个月发布了 4 个；首次发布于 2020/07/15。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

