- 后端
- 数据库
- ORM
【免费下载链接】drizzle-orm
ORM
导读
本文聚焦 drizzle-kit 0.31.7 发布说明中的唯一一条变更:修复了drizzle-kit push在 PostgreSQL 18 上、当 schema 实际未发生任何变更时,仍然生成多余 DROP SQL 的 bug(对应 issue #4944)。文章将结合 drizzle-kit 仓库源码,完整拆解push命令从数据库 introspection、schema 快照序列化、快照差异比较到 DROP 语句生成与数据丢失拦截的整条调用链,帮助你理解"多余 DROP"从何而来、为何危险、以及升级到 0.31.7 后如何验证修复是否生效。
修复背景:0.31.x 系列中的一次针对性 Bugfix
drizzle-kit 0.31.7是 0.31 稳定版本线的后续补丁版本,官方变更说明(changelogs/drizzle-kit/0.31.7.md)内容非常聚焦:
- 修复
drizzle-kit push在 PostgreSQL 18 下、schema 未发生任何修改时,产生不必要(unnecessary)DROP SQL 的问题。
理解这条修复需要先把它放回版本脉络中。0.31.x 系列此前经历了不少结构性与体验性改进:
- 0.31.0:Enum DDL 改进(删除/重排 enum 值时切换列类型并重建默认表达式)、esbuild 升级到 0.25.2;
- 0.31.1:修复使用 Gel extensions(含
::的 schema 名,如ext::auth)时pull的问题; - 0.31.2:修复关系提取与 Drizzle Studio 相互干扰的问题;
- 0.31.3:Studio 内部上下文增加
databaseName与packageName; - 0.31.4:修复
halfvec、bit、sparsevec类型生成 bug; - 0.31.6:修复 drizzle-kit/api 在 ESM 模块中导入失败的问题。
也就是说,0.31.7 之前的多个版本都在调整 introspection、类型生成与 Studio 行为,而 0.31.7 单独把 PostgreSQL 18 上的 push 误报 DROP 问题拎出来修复,说明这是直接影响日常push流程可靠性的一类缺陷。随后 0.31.8 继续修复了algorythm→algorithm拼写与构建外部依赖问题,0.31.10 引入tsxloader 并支持原生 Bun/Deno 启动,可见 0.31.7 处于 0.31.x 中期"把 push 做稳"的阶段。
drizzle-kit push到 PostgreSQL 的完整工作链路
要理解"多余 DROP",必须先知道push命令在 PostgreSQL 上的执行流水线。仓库中 PostgreSQL push 的入口实现在 drizzle-kit/src/cli/commands/push.ts#L289-L379 的pgPush函数,其核心步骤依次为:
- 读取数据库当前状态(introspection):调用
pgPushIntrospect(见 drizzle-kit/src/introspect-pg.ts),连接真实数据库,把库中的表、列、索引、约束、enum、policy 等读取为"数据库侧快照"; - 序列化 Schema 文件:调用
serializePg(schemaPath, casing, schemasFilter)(见 drizzle-kit/src/serializer/index.ts 对应的 pg 序列化实现),把 TypeScript schema 文件编译执行后解析成"期望侧快照";preparePgDbPushSnapshot在 drizzle-kit/src/migrationPreparator.ts#L64-L79 中完成两侧快照与prevId的装配; - 快照差异比较:调用
preparePgPush(drizzle-kit/src/cli/commands/migrate.ts),内部经过applyPgSnapshotsDiff(drizzle-kit/src/snapshotsDiffer.ts)逐表、逐列、逐索引比较两个快照,产出结构化变更语句(JsonStatement数组); - 数据丢失风险评估:调用
pgSuggestions(drizzle-kit/src/cli/commands/pgPushUtils.ts#L59-L269),对drop_table、alter_table_drop_column、alter_table_alter_column_set_type、drop_schema等危险语句做select count(*)检查,非空表/列会打印告警并要求二次确认; - 执行:确认通过后逐条执行 SQL。
如果第 3 步判定两侧快照完全一致,pgPush会走render('[i] No changes detected')分支直接结束,不会执行任何 SQL(见 push.ts#L316-L318)。因此,一旦出现"多余 DROP",必然意味着第 1 步或第 3 步出了问题:要么 introspection 拿到的数据库快照与真实库不一致,要么差异比较逻辑把"无差异"误判成了"有差异"。
多余 DROP 的生成路径:从 diff 到DROP TABLE ... CASCADE
当差异比较确实判定某个表被删除时,DROP 语句是这样产生的:
- drizzle-kit/src/jsonStatements.ts#L1005-L1012 的
prepareDropTableJson生成结构化语句:
export const prepareDropTableJson = (table: Table): JsonDropTableStatement => { return { type: 'drop_table', tableName: table.name, schema: table.schema, policies: table.policies ? Object.values(table.policies) : [], }; };- drizzle-kit/src/sqlgenerator.ts#L1517-L1546 的
PgDropTableConvertor将其翻译为 SQL,同时会先为表上关联的 RLS policy 生成 drop policy 语句,最后追加:
DROP TABLE "schema"."table" CASCADE;注意这里的CASCADE:对于被其他表外键引用的表,会连带删除依赖。也就是说,"多余的 DROP"不是一句无伤大雅的清理,而可能波及真实存在的表结构。
- 在 push 的交互确认环节,
pgSuggestions会对drop_table语句先执行select count(*) as count from "schema"."table",若行数大于 0,则输出类似You're about to delete <table> table with <count> items的告警,并把该表加入tablesToRemove,同时要求用户在"Abort / 继续删除"之间选择(见 pgPushUtils.ts#L78-L92 与 push.ts#L354-L380)。
因此 #4944 反映的典型症状是:用户在 PostgreSQL 18 上执行drizzle-kit push,明明没改 schema,终端却弹出"要删除某张表"的确认(甚至直接提示会造成数据丢失),迫使用户反复 abort,严重干扰开发流程,也容易因误操作真正删掉表。
为什么 PostgreSQL 18 会触发误判:从源码结构推断的可能原因
该 changelog 只描述了现象与结论(在 PG 18 上、schema 未变更却生成多余 DROP),并未公开根因细节。基于仓库代码结构可以作如下谨慎推断:
- 差异比较依赖 introspection 结果的"稳定可复现":
pgPushIntrospect的输入是数据库系统表/信息模式查询结果,任何由数据库版本引起的元数据表示差异(例如信息模式中默认值格式化、类型名表示、约束命名规则的差异)都会反映进"数据库侧快照"; - 快照结构升级逻辑集中在
pgUp.ts:drizzle-kit/src/cli/commands/pgUp.ts#L44-L110 中可见 drizzle-kit 的 PostgreSQL 快照 schema 经历过 v5→v6(表/枚举键加入 schema 前缀)、v6→v7(索引列格式从纯字符串改为{ expression, isExpression, asc, nulls, opClass }对象)等多次演进。当数据库侧快照由新版本 PostgreSQL 的 introspection 产生、而序列化侧快照仍按旧格式组装时,字段级比较就可能产生伪差异; - 类型/表达式表示的方言差异:例如
timestamp精度表示、numeric默认精度、enum 默认值中::text与::<enum>的写法差异(后者正是 0.31.0 引入的新行为,见 changelogs/drizzle-kit/0.31.0.md)如果未能在 introspection 侧做等价归一化,就会被 diff 视为"需要重建列"乃至"需要重建表"。
以上属于"从源码结构推断"层面的分析,具体修复点应以其官方发布说明与对应 commit 为准;无论如何,0.31.7 之后该场景不再产生多余的 DROP SQL。
如何在你的环境中复现与验证修复
复现前提
- 数据库为 PostgreSQL 18;
- 使用与 0.31.6 及更早版本相近的 drizzle-kit(0.31.7 修复前的行为),执行
drizzle-kit push且不修改任何 schema 文件,观察是否弹出删除表/列的确认; - 典型触发面:项目中存在 enum、带默认值的列、JSON/JSONB 索引或自定义 schema 时更容易命中(这些正是 0.31.0~0.31.4 反复调整过的对象类型)。
验证修复
- 将
drizzle-kit升级到0.31.7或更高(当前仓库 drizzle-kit/package.json 中版本已迭代至 0.31.11); - 在 schema 零修改的前提下运行
drizzle-kit push,应稳定输出[i] No changes detected; - 若仍怀疑有差异,可配合
drizzle-kit push --verbose查看将要执行的完整语句清单(对应 push.ts#L331-L340 中statementsToExecute的打印逻辑),确认列表为空或仅含预期变更; - 仓库的 push 测试基础设施位于 drizzle-kit/tests/push/pg.test.ts,它基于
@electric-sql/pglite内存实例配合diffTestSchemasPush(drizzle-kit/tests/schemaDiffer.ts)直接验证快照差异生成语句的正确性,是排查"多余 DDL"类回归的首选参考。
交互确认的防护提醒
无论版本如何,涉及 DROP 的 push 都存在数据丢失风险。pgSuggestions对以下场景会强制要求确认(见 pgPushUtils.ts):
drop_table:表内行数 > 0;alter_table_drop_column:表内行数 > 0;alter_table_alter_column_set_type:需要截断表(truncate table ... cascade);alter_table_add_column:新增 not-null 列且无默认值时表内有数据;drop_schema:schema 内仍有表。
在升级到 0.31.7 之后,如果你在"未改 schema"时仍看到这些确认,说明差异检测依然发现了真实差异,此时应优先通过--verbose查看具体语句,而不是盲目--force跳过确认。
小结
drizzle-kit 0.31.7修复的核心是 PostgreSQL 18 场景下push的"伪差异"误判:当 introspection 得到的数据库快照与 schema 序列化快照因版本差异未对齐时,diff 层会生成多余的DROP TABLE ... CASCADE语句,把本应输出No changes detected的正常流程变成高危确认。修复的生效形态是——schema 未变更时,push稳定报告无变更、不执行任何破坏性 SQL。对于正在使用 PostgreSQL 18 并频繁push的团队,将 drizzle-kit 升至 0.31.7 及以上即可避开该问题;如需深入排查或回归验证,可围绕 push.ts、pgPushUtils.ts、snapshotsDiffer.ts 与 tests/push/pg.test.ts 展开。
- 后端
- 数据库
- ORM
【免费下载链接】drizzle-orm
ORM
相关推荐
Drizzle Kit 0.31.1:全面修复 Gel 扩展 Schema 的 `drizzle-kit pull` 逆向导入问题
Drizzle Kit 0.31.1:全面修复 Gel 扩展 Schema 的 drizzle kit pull 逆向导入问题 本篇指南围绕 Drizzle K
后端数据库ORMPrettier 变更解析:修复 Angular 插值(Interpolation)中多余空行的生成问题
Prettier 变更解析:修复 Angular 插值(Interpolation)中多余空行的生成问题 本篇文章基于 Prettier 仓库中 changel
开发工具格式化CLIDrizzle Kit 迁移生成器实战指南:Schema 快照对比与自动 SQL 迁移
Drizzle Kit 迁移生成器实战指南:Schema 快照对比与自动 SQL 迁移 Drizzle Kit 是 Drizzle ORM 官方提供的 CLI
后端数据库ORM
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考