|
| 1 | +--- |
| 2 | +"@objectstack/driver-sql": patch |
| 3 | +--- |
| 4 | + |
| 5 | +`updateMany()` and `upsert()`'s merge branch now advance `updated_at` (#11176). |
| 6 | + |
| 7 | +Two write doors were leaving "last modified" reading the row's previous value — |
| 8 | +on **every** deployment, DDL-managed or not (which is what separates these from |
| 9 | +#11067, whose defect needed `skipSchemaSync`): |
| 10 | + |
| 11 | +- **`updateMany()` stamped nothing.** No `tablesWithTimestamps` consultation, no |
| 12 | + `updated_at`. `update()`, `bulkUpdate()` and `rotatedUpdateById()` all stamp; |
| 13 | + this door did not, so a bulk edit left every row it touched reading its |
| 14 | + creation time. |
| 15 | +- **`upsert()`'s merge branch did not advance it on Postgres and MySQL.** The |
| 16 | + merge set is derived from the keys of the formatted payload, and |
| 17 | + `stampInsertTimestamps` — the only thing that put `updated_at` there — returns |
| 18 | + early on any non-SQLite dialect, because the column DEFAULT already stores a |
| 19 | + zone-aware instant on insert. A DEFAULT does not re-fire on the conflict path. |
| 20 | + **SQLite was accidentally correct; Postgres and MySQL were not.** |
| 21 | + |
| 22 | +Measured on live PostgreSQL 16.13 and MySQL 8.0.46, through the driver's own |
| 23 | +`initObjects`, with `update()` advancing the same column on the same table in |
| 24 | +the same run as the contrast. |
| 25 | + |
| 26 | +Nothing errored before this fix, which is why it went unnoticed: list-view |
| 27 | +sorts, delta/incremental sync, cache invalidation and audit answers simply read |
| 28 | +a stale `updated_at`. A bulk status change and a sync/import that upserts — the |
| 29 | +common shape for connector and seed writes — are exactly the operations most |
| 30 | +likely to be feeding a downstream delta consumer. |
| 31 | + |
| 32 | +No accept-set change and no new rejection. Both doors keep #3493's opt-in |
| 33 | +historical import (`preserveAudit` with an explicit `updated_at`) intact, and a |
| 34 | +hand-migrated table that genuinely lacks the column keeps working: `updateMany` |
| 35 | +reuses #11067's presumption-and-recovery machinery whole, and the upsert door |
| 36 | +stamps only where the column has been OBSERVED, never on a presumption it has no |
| 37 | +way to recover from. The SQL emitted for SQLite, and for any object with no |
| 38 | +observed `updated_at` column, is unchanged. |
0 commit comments