ReviewOS

stacks/bun-query-builder

fix(orm): an update that sets updated_at assigns it twice, which Postgres refuses

#1196
Closed glennmichael123 opened this 7 days ago ·
7 days ago

Found while fixing stacksjs/stacks#3014.

On a timestamped model (useTimestamps / timestampable), both ModelQueryBuilder.update(data) and an instance's save() append updated_at = ? with the time of the write, without checking whether the caller had already set updated_at. An update that names it therefore sends the column twice:

UPDATE posts SET title = ?, updated_at = ?, updated_at = ? WHERE ...
  • SQLite keeps the rightmost assignment and MySQL applies them left to right, so on both the managed time wins quietly.
  • PostgreSQL refuses the statement: multiple assignments to same column "updated_at".

So in a Stacks app, Model.update(id, { updated_at }) works on SQLite and MySQL and fails on PostgreSQL. The same happens with where(...).update({ updatedAt }), which the builder snake-cases, and with forceFill({ updated_at }); save().

Fix. The write owns updated_at, as it already does on insert (orm.explicit-created-at.test.ts pins "still stamps updated_at with the time of the write"). So a caller's updated_at, in either spelling, is left out of the SET list and the managed one is the only assignment. An update that names only updated_at still writes it, as the time of the write. PostgreSQL now behaves as SQLite and MySQL already did.

Test. test/orm.managed-updated-at.pg.test.ts runs against live Postgres and covers four cases: an update with updated_at, one with updatedAt, an update of updated_at alone, and save() with a changed updated_at. All four throw before the fix and pass after it.

Sign in to comment on this issue.