ReviewOS

stacks/bun-query-builder

ORM: Postgres UPDATE/DELETE report 0 affected rows (extractChanges falls back to array length)

#1032
Closed glennmichael123 opened this 2 months ago ·
2 months ago

Summary

extractChanges() checks affectedRows (MySQL), then res.count, then Array.isArray → res.length. For a Postgres UPDATE/DELETE via Bun.sql.unsafe (no RETURNING), the result is typically an empty array, so res.length === 0 and the function returns 0 even when rows were affected. That makes ModelQueryBuilder.update()/delete(), destroy(), and BelongsToManyRelationBuilder.detach()/updateExistingPivot() report 0/false despite succeeding.

Status

Suspected — derived from code + Bun result-shape reasoning; not yet reproduced against a live Postgres (no server in the audit sandbox). Needs confirmation of Bun's non-RETURNING command-tag shape.

Location

src/orm.ts:~355 (extractChanges) and ~424.

Suggested fix

Read the Postgres command-tag affected-row count (Bun exposes it on the result object, not as array length) and prefer it over length.

Severity: medium. Found via internal code audit.

9 days ago

Verified NOT reproducible against live Postgres (closing as not-a-bug). Bun 1.3 returns a non-RETURNING UPDATE/DELETE as an empty array carrying { count: <affected>, affectedRows: null, command: 'UPDATE' }, and extractChanges already checks count before the Array.length fallback (affectedRows: null is correctly skipped since typeof null !== 'number'). End-to-end through the ORM: update() returned 2 and delete() returned 1 against a live PG. Added shape-based unit tests (4f3ae8b) pinning the check ordering so it can't regress.

Sign in to comment on this issue.