Skip to content

v3.9.0

Choose a tag to compare

@lehni lehni released this 12 Oct 12:45

Fixes

  • Behaviour change: upsertGraph() replacing the row of a BelongsToOneRelation, e.g. { id: 1, pet: { id: 2 } }, points the owner to the new row before it deletes the old one. The static delete hooks of these deletes get no relation or items in their context, as the rows are deleted by id. Before, the old row was deleted first: with a NOT NULL foreign key and ON DELETE CASCADE, the database deleted the owner and upsertGraph() still succeeded, and foreign keys without ON DELETE action failed. A new row that repeats a unique value of the row it replaces now fails, as both exist at the same time: pass the id of the old row to update it instead. When noInsert or noRelate skips the replacement, the current row and the owner's reference are kept.
  • Behaviour change: upsertGraph() removing the row of a BelongsToOneRelation with pet: null or #delete sets the owner's foreign key to null before it deletes the row if the owner's jsonSchema allows null for it (in type, anyOf, oneOf or with nullable: true). Before, the row was deleted first: with ON DELETE CASCADE the database deleted the owner, and foreign keys without ON DELETE action failed. The owner is now patched, also with ON DELETE SET NULL, so its update hooks run (e.g. writing updatedAt), and the static delete hooks of these deletes, like those of replaced rows, get no relation or items in their context. Without a nullable schema for the key, the row is still deleted first and the owner's reference is left to the database. #212
  • Behaviour change: upsertGraph() no longer deletes a row that is missing in one place of the graph if the same graph relates or updates it in another place. The row is unrelated from its old place instead, so rows can be moved between parents in one call, also below related models. #214
  • Behaviour change: upsertGraph() with a #unrelate or #delete marker on a BelongsToOneRelation model that isn't the currently related one keeps the current one, as the marked model is ignored. Before, the current row was deleted without clearing the owner's foreign key. #213
  • Behaviour change: insertGraph() and upsertGraph() no longer insert the related models of models that noInsert skips, unless a #ref points to them. These were inserted without foreign keys. #215
  • insertGraph() logs a warning when it ignores new models below related models. This will throw in objection 4.0. Use upsertGraph() or a separate query to insert them. #217
  • Behaviour change: the graph options reject * and ^ in brackets with other relations, like children.[pets, ^], instead of never matching them #221
  • The #ref and #dbRef markers are removed from the results of insertGraph() and upsertGraph(), also from models they skip #221
  • upsertGraph() no longer throws "model (id=…) is not a child of model (id=…)" for children with ids below nodes related through a relate path option #220
  • $relatedQuery().insertGraph() with several models on many-to-many relations inserts the join rows on SQLite and MySQL, where it failed #219
  • $relatedQuery().insertGraph() on a BelongsToOneRelation relates the inserted row to the owner #216
  • Behaviour change: deleteById(), patchById() and updateById() also keep their id condition in its own where group, which the 3.8.0 notes already claimed for "the other *ById methods": deleteById(1).where(a).orWhere(b) runs id = 1 and (a or b) instead of id = 1 and a or b, which deleted other rows. A findById() added by the user stays in the user's where group, also in findById(1).patch(…). #232
  • Behaviour change: a many-to-many patch() / update() that only changes join table extras returns the number of related rows whose join rows it updated, instead of 0. The static afterUpdate hooks receive this number as their result instead of 0. patchAndFetchById() with only extras returns the patched row instead of undefined. #235
  • Behaviour change: on SQL Server, a many-to-many patch() / update() with join table extras applies limit(), offset() and orderBy() to the related rows it picks, and only updates their join rows, as on MySQL. Before, the limit applied to each batch of related rows, all join rows were updated, and an orderBy() with the limit failed with a syntax error. With limit().offset() but no orderBy() it now throws, as SQL Server rejects OFFSET without ORDER BY, where the offset was ignored before. #233
  • Many-to-many patch() / update() with join table extras select the related ids straight from their filter query, unless limit() or offset() picks the related rows on MySQL or SQL Server. Postgres could plan the previous query as a nested loop that ran for minutes #161
  • On MySQL, delete() with both orderBy() and limit() warns that the order has no effect, as knex leaves it out of the statement. This will throw in objection 4.0. #235
  • Behaviour change: query properties like raw(), ref() and subqueries given to a model instance are no longer written again once their keys are given a value, by assignment, $set(), $setJson() or a write that fetched the value. $setJson() keeps the query properties of earlier calls, $setJson(modelInstance) drops them for the keys the instance has, values that objection sets itself (e.g. foreign keys set by insertGraph() and $relatedQuery().insert()) beat a query property for the same key, values that $beforeInsert() / $beforeUpdate() set for keys given as query properties are removed from the instance after the hooks, as the query properties are written instead, and schema defaults no longer appear on the model or in toJSON() for keys given as query properties. #202
  • raw() and other query properties assigned to json attributes in $beforeInsert() and $beforeUpdate() are written instead of their serialized objects #203
  • Behaviour change: pickJsonSchemaProperties keeps field expression keys like 'meta:a' whose json column is in the schema, so patch(), update() and upsertGraph() write them instead of silently dropping them. insert() no longer silently drops such keys either: they reach the database and fail as they do without pickJsonSchemaProperties. Keys only found on Object.prototype (like 'toString') are no longer picked. upsertGraph() no longer selects field expression keys as columns. #204
  • Behaviour change: a patch(), update() or merge() that sets both a json column and a field expression into it throws a ValidationError, also when a model instance holds both, instead of dropping the field expression or failing with an SQL error #206
  • Behaviour change: patches and the field expressions of update() are validated without the required properties of schemas referenced by $id, like the ones added in onCreateAjv(), which regressed in 3.8.0. Whole values for such properties in patches are validated without required too, like local definitions. #207
  • onConflict().merge() with a json field expression key no longer fails with an ambiguous column on Postgres. merge({ 'meta:b': ref('meta:a') }) is still ambiguous: qualify the reference with the table, e.g. ref('items.meta:a'). #260
  • Behaviour change: hasSelection() and findSelection() treat a table name without schema as the schema-qualified table, so withGraphJoined() no longer throws for selections like persons.id of a model with a tableName like public.persons, a regression in 3.8.0, and replaces its persons.* selections #226
  • Behaviour change: withGraphJoined() tells apart rows whose ids are Buffers, e.g. binary UUIDs, and withGraphJoined() / withGraphFetched() tell apart keys whose dates differ only in milliseconds, composite keys whose strings contain commas, and string ids that contain / or ., also in upsertGraph(). Dates are keyed by their time, so a Date and a number with the same epoch milliseconds match, e.g. Date ids passed to fetchGraph() and the numbers SQLite returns for them. #222 #223
  • resultSize() and the total of page() and range() respect distinctOn() with withGraphJoined() on Postgres, also on joined columns #224
  • limitPerParent limits the related items when the relation key is a JSON field #225
  • withGraphFetched() with the limitPerParent option works for related models with a schema-qualified tableName, also when the order names the schema #265
  • Behaviour change: when SQL Server reports several errors for one statement, e.g. for offset() without orderBy(), objection throws a DBError with all their messages, instead of the driver's AggregateError with an empty message, which is kept as nativeError
  • update(), patch() and delete() warn about offset(), as knex leaves it out there, and on SQL Server the limit too. This will throw an error in objection 4.0. #286

Types

  • Custom query builders that override withGraph() compile again, also with TypeScript 7, which broke in 3.7.0 with the relate().returning() typings

Docs

  • withGraphFetched() doesn't assign related rows whose keys only match by collation: by case or accents on MySQL, by case or trailing spaces on SQL Server #227
  • upsertGraph() matches ids by their exact value, and how to normalize uuids given in a different case #221