Skip to content

insert 路径 stripRuntimeOwnedFields 同样删的是 hook 覆写后的当前值 —— beforeInsert hook 写入的 autonumber 在调用方也提交了同名键时被连坐删除(与其自身文档矛盾) #6339

Description

@baozhoutao

发现于 #5591(update 面同类缺陷)的实测过程,insert 面的对称问题。#5591 的界限明确不含 insert,故独立立单,非其 sub-issue(修法不在 #5591 的完成范围内,也不依赖它先合)。

现象

stripRuntimeOwnedFields(packages/objectql/src/validation/rule-validator.ts)与 update 面的 stripReadonlyFields 用的是同一种判据:suppliedKeys 只是键名集合。而 engine.ts insert 路径上:

  • triggerHooks('beforeInsert', rowCtx)engine.ts:5318
  • rowsrowHookContextsinput.data 映射而来,在 engine.ts:5341
  • suppliedPerRow 快照在 engine.ts:5374
  • 剥离在 engine.ts:5397-5412,delete result[name] 删的是执行到剥离时的当前值

于是 hook 若覆写了一个调用方也提交过的 runtime-owned 键,被删掉的是 hook 的写入,而不是调用方提交的值。

实测(worktree @ origin/main cca11e9d3,真 ObjectQL + InMemory 驱动)

对象 probe_num2 { title: text, code: autonumber }
beforeInsert hook: ctx.input.data.code = 'HOOK-OVERWRITE'

A) 调用方不提交 code                => 落库 code = "HOOK-1"     (hook 写入存活,符合文档)
B) 调用方提交 code='CALLER-FORGED'  => 落库 code = "1"          (hook 写入被删,序列补发)

B 与该函数自己的文档直接矛盾 —— runtimeOwnedStripWarning() 的正文写着:

A beforeInsert/beforeUpdate hook does NOT need either — hook-written keys are not caller-supplied.

A 与 B 的差别只有「调用方 payload 里是否碰巧出现同名键」,与 #5591 在 update 面记录的是同一条判据错误。

影响

窄但真实:一个在 beforeInsert 里重新签发/归一化记录号的 hook,只要调用方同时提交了该键,其写入就被静默丢弃并回落到序列值。整记录 POST(读模板 → 改字段 → 整体提交)会触发。今日无已知线上用例,故未自评严重度,交分诊定级。

期望

#5591 同一判据:剥离作用于调用方提交的值,不作用于 hook 覆写后的当前值。#5591 在 update 面的修法(入口快照携带调用方的值,剥离时以 Object.is 比对身份;非导出函数,签名可自由收敛)可直接照搬到 stripRuntimeOwnedFields + engine.tssuppliedPerRow

注意 insert 面有一处 update 面没有的细节需先确认:suppliedPerRow 取自 opCtx.data,而 hook 是原地ctx.input.data;两者是否同一引用决定了快照是否必须显式浅拷贝(update 面 #5591 已因此显式 spread)。上面的 A 用例说明 hook 新增的键当前不在快照里,所以至少新增面是干净的。

边界


Blocked-by: #5591


Generated by Claude Code

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions