2021.12 v2.0

Both v1.15.1 and v2.0.0 were released this month. Breaking changes were introduced in the new major version, v2.x.

v2.x Reminders

Before upgrading to v2.0.0, please be noted that methods like logQueryError(err, sql, duration, options) and model.sync({ force | alter }) have changed, which has been noticed earlier in the 2021.11 release notes)

logQueryError()

diff --git a/app.js b/app.js
index a6008a597..6a3647ec9 100644
--- a/app.js
+++ b/app.js
@@ -36,7 +36,7 @@ module.exports = class AppBootHook {
           const table = Model ? Model.table : '';
           app.logger.info(`leoric^success^${duration}^${modelName}^${table}^${command}^${sql}`);
         },
-        logQueryError(sql, err, duration, options) {
+        logQueryError(err, sql, duration, options) {
           const { command = '', Model } = options;
           const modelName = Model ? Model.name : '';
           const table = Model ? Model.table : '';

This breaking change can be omitted if logQueryError() weren’t used in existing code.

model.sync()

diff --git a/app.js b/app.js
index 600f40eecf..84063cb1ca 100644
--- a/app.js
+++ b/app.js
@@ -100,7 +100,7 @@ async function connect(app, db) {
   }

   try {
-    await db.sync();
+    await db.sync({ alter: true });
   } catch (err) {

The default is model.sync({ force: false, alter: false }), hence model.sync() calls won’t touch existing tables in v2.x, rather than switch to ALTER TABLE by default in v1.x. Please change from model.sync() to model.sync({ alter: true }) if ALTER TABLE is still preferred.

It is highly NOT recommended using this method in production, especially not model.sync({ force }), which would DROP TABLE IF EXISTS ... to skip the hard work.

It is recommended using Migrations to manage database.

v1.15.1

https://github.com/cyjake/leoric/releases/tag/v1.15.1

Model.join().limit()

https://github.com/cyjake/leoric/pull/247

In previous versions, following query:

await Post.include('comments')
  .order('comments.id', 'desc')
  .order('posts.id', 'desc')
  .limit(1);

is equivalent to following SQL:

SELECT posts.*, comments.*
  FROM (SELECT * FROM posts ORDER BY id DESC)
    AS posts
  LEFT JOIN comments ON posts.id = comments.post_id
 ORDER BY comments.id desc

which is in order to guarantee that the first Post can be retrived correctly. But in actual scenarios, the ambiguity of this query makes this ORDER BY and LIMIT hoisting a premature optimization. For example, if the records of Post were like:

post_id comment_id
1 3
2 2
3 1

the query should return Post <#id 1> but actually it returns Post <#id 3>. Hence this behavior is removed in v1.15.1.

Date Casting

If the model didn’t specify precision of DATETIME, the precision casting will be skipped in previous versions. In v1.15.1, the precision returned from information_schema.columns will be used. For example, in following model:

class Post {
  static attributes = {
    createdAt: DATE
  }
}

the column type of posts.created_at might be DATETIME(0), which is the default in MySQL, or it could be TIMESTAMP(6) (recommended default by some DMS tools ). Since v1.15.1, these preferences will be recognized and added as complimentary metadata to Post.attributes.createdAt.

With the date precision complemented, following operations trigger date casting:

const post = new Post({ createdAt: new Date() });
post.createdAt = new Date('2021-12-31');
const post2 = await Post.create({ createdAt: new Date('2021-12-31') });

There won’t be much of casting work to do, besides following situations:

options.silent

When updating the updatedAt timestamp itself with options.silent set to true, there was this issue that the update won’t take effect, which is fixed in both v1.15.1 and v2.0.0.

const post = await Post.first;
await post.update({ updatedAt: new Date('2021-10-15') }, { silent: true });