leoric 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/adapters/sequelize.js +17 -4
- package/src/bone.js +64 -46
- package/src/collection.js +1 -1
- package/src/constants.js +30 -0
- package/src/data_types.js +17 -9
- package/src/drivers/abstract/attribute.js +3 -3
- package/src/drivers/abstract/spellbook.js +4 -2
- package/src/drivers/mysql/spellbook.js +32 -0
- package/src/drivers/postgres/data_types.js +26 -0
- package/src/drivers/postgres/schema.js +6 -1
- package/src/drivers/sqlite/data_types.js +42 -0
- package/src/drivers/sqlite/schema.js +5 -3
- package/src/expr_formatter.js +1 -1
- package/src/realm.js +2 -8
- package/src/spell.js +1 -1
- package/History.md +0 -550
- package/src/contants.js +0 -13
package/package.json
CHANGED
|
@@ -238,9 +238,6 @@ module.exports = Bone => {
|
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
static build(values, options = {}) {
|
|
241
|
-
if (options.validate !== false) {
|
|
242
|
-
this._validateAttributes(values);
|
|
243
|
-
}
|
|
244
241
|
const { raw } = Object.assign({ raw: false, isNewRecord: true }, options);
|
|
245
242
|
const { attributes } = this;
|
|
246
243
|
|
|
@@ -256,9 +253,21 @@ module.exports = Bone => {
|
|
|
256
253
|
} else {
|
|
257
254
|
instance = new this(values, options);
|
|
258
255
|
}
|
|
256
|
+
|
|
259
257
|
return instance;
|
|
260
258
|
}
|
|
261
259
|
|
|
260
|
+
/**
|
|
261
|
+
* see https://github.com/sequelize/sequelize/blob/a729c4df41fa3a58fbecaf879265d2fb73d80e5f/src/model.js#L2299
|
|
262
|
+
* @param {Array<Object>} valueSets
|
|
263
|
+
* @param {Object} options
|
|
264
|
+
* @returns
|
|
265
|
+
*/
|
|
266
|
+
static bulkBuild(valueSets, options = {}) {
|
|
267
|
+
if (!valueSets.length) return [];
|
|
268
|
+
return valueSets.map(value => this.build(value, options));
|
|
269
|
+
}
|
|
270
|
+
|
|
262
271
|
// EXISTS
|
|
263
272
|
// static bulkCreate() {}
|
|
264
273
|
|
|
@@ -310,7 +319,9 @@ module.exports = Bone => {
|
|
|
310
319
|
// proxy to class.destroy({ individualHooks=false }) see https://github.com/sequelize/sequelize/blob/4063c2ab627ad57919d5b45cc7755f077a69fa5e/lib/model.js#L2895 before(after)BulkDestroy
|
|
311
320
|
static async bulkDestroy(options = {}) {
|
|
312
321
|
const { where, force } = options;
|
|
313
|
-
|
|
322
|
+
const spell = this._remove(where || {}, force, { ...options });
|
|
323
|
+
translateOptions(spell, options);
|
|
324
|
+
return spell;
|
|
314
325
|
}
|
|
315
326
|
|
|
316
327
|
// EXISTS
|
|
@@ -469,6 +480,7 @@ module.exports = Bone => {
|
|
|
469
480
|
const { where, paranoid, individualHooks } = options;
|
|
470
481
|
if (individualHooks) {
|
|
471
482
|
let findSpell = this._find(where, options);
|
|
483
|
+
translateOptions(findSpell, options);
|
|
472
484
|
if (paranoid === false) findSpell = findSpell.unparanoid;
|
|
473
485
|
const instances = await findSpell;
|
|
474
486
|
if (instances.length) {
|
|
@@ -484,6 +496,7 @@ module.exports = Bone => {
|
|
|
484
496
|
const { where, paranoid = false, validate } = options;
|
|
485
497
|
const whereConditions = where || {};
|
|
486
498
|
const spell = super.update(whereConditions, values, { validate, hooks: false, ...options });
|
|
499
|
+
translateOptions(spell, options);
|
|
487
500
|
if (!paranoid) return spell.unparanoid;
|
|
488
501
|
return spell;
|
|
489
502
|
}
|
package/src/bone.js
CHANGED
|
@@ -15,6 +15,7 @@ const Raw = require('./raw');
|
|
|
15
15
|
const { capitalize, camelCase, snakeCase } = require('./utils/string');
|
|
16
16
|
const { hookNames, setupSingleHook } = require('./setup_hooks');
|
|
17
17
|
const { logger } = require('./utils/index');
|
|
18
|
+
const { TIMESTAMP_NAMES, LEGACY_TIMESTAMP_COLUMN_MAP } = require('./constants');
|
|
18
19
|
|
|
19
20
|
function looseReadonly(props) {
|
|
20
21
|
return Object.keys(props).reduce((result, name) => {
|
|
@@ -76,6 +77,23 @@ function copyValues(values) {
|
|
|
76
77
|
return copyValue;
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
function valuesValidate(values, attributes, ctx) {
|
|
81
|
+
for (const valueKey in values) {
|
|
82
|
+
const attribute = attributes[valueKey];
|
|
83
|
+
if (!attribute) continue;
|
|
84
|
+
const { validate = {}, name, allowNull, defaultValue } = attribute;
|
|
85
|
+
const value = values[valueKey];
|
|
86
|
+
if (value == null && defaultValue == null) {
|
|
87
|
+
if (allowNull === false) throw new LeoricValidateError('notNull', name);
|
|
88
|
+
if ((allowNull === true || allowNull === undefined) && validate.notNull === undefined ) continue;
|
|
89
|
+
}
|
|
90
|
+
if (!validate) continue;
|
|
91
|
+
for (const key in validate) {
|
|
92
|
+
if (validate.hasOwnProperty(key)) executeValidator(ctx, key, attribute, value);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
79
97
|
/**
|
|
80
98
|
* The base class that provides Object-relational mapping. This class is never intended to be used directly. We need to create models that extends from Bone. Most of the query features of Bone is implemented by {@link Spell} such as {@link Spell#$group} and {@link Spell#$join}. With Bone, you can create models like this:
|
|
81
99
|
*
|
|
@@ -134,7 +152,6 @@ class Bone {
|
|
|
134
152
|
}
|
|
135
153
|
}
|
|
136
154
|
|
|
137
|
-
|
|
138
155
|
/**
|
|
139
156
|
* Get or set attribute value by name. This method is quite similiar to `jQuery.attr()`. If the attribute isn't selected when queried from database, an error will be thrown when accessing it.
|
|
140
157
|
*
|
|
@@ -280,21 +297,7 @@ class Bone {
|
|
|
280
297
|
|
|
281
298
|
// merge all changed values
|
|
282
299
|
changedValues = Object.assign(changedValues, values);
|
|
283
|
-
|
|
284
|
-
for (const valueKey in changedValues) {
|
|
285
|
-
const attribute = attributes[valueKey];
|
|
286
|
-
if (!attribute) continue;
|
|
287
|
-
const { validate = {}, name, allowNull, defaultValue } = attribute;
|
|
288
|
-
const value = changedValues[valueKey];
|
|
289
|
-
if (value == null && defaultValue == null) {
|
|
290
|
-
if (allowNull === false) throw new LeoricValidateError('notNull', name);
|
|
291
|
-
if ((allowNull === true || allowNull === undefined) && validate.notNull === undefined ) return;
|
|
292
|
-
}
|
|
293
|
-
if (!validate) return;
|
|
294
|
-
for (const key in validate) {
|
|
295
|
-
if (validate.hasOwnProperty(key)) executeValidator(this, key, attribute, value);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
300
|
+
valuesValidate(changedValues, attributes, this);
|
|
298
301
|
}
|
|
299
302
|
|
|
300
303
|
/**
|
|
@@ -306,22 +309,7 @@ class Bone {
|
|
|
306
309
|
*/
|
|
307
310
|
static _validateAttributes(values = {}) {
|
|
308
311
|
const { attributes } = this;
|
|
309
|
-
|
|
310
|
-
const attribute = attributes[valueKey];
|
|
311
|
-
// If valueKey is not an attribute of the Model, go to the next loop instead of throw 'No Such Attribute' Error,
|
|
312
|
-
// in case it is a custom property of the Model which defined by custom setters/getters.
|
|
313
|
-
if (!attribute) return;
|
|
314
|
-
const { validate = {}, name, allowNull, defaultValue } = attribute;
|
|
315
|
-
const value = values[valueKey];
|
|
316
|
-
if (value == null && defaultValue == null) {
|
|
317
|
-
if (allowNull === false) throw new LeoricValidateError('notNull', name);
|
|
318
|
-
if ((allowNull === true || allowNull === undefined) && validate.notNull === undefined) return;
|
|
319
|
-
}
|
|
320
|
-
if (!validate) return;
|
|
321
|
-
for (const key in validate) {
|
|
322
|
-
if (validate.hasOwnProperty(key)) executeValidator(this, key, attribute, value);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
312
|
+
valuesValidate(values, attributes, this);
|
|
325
313
|
}
|
|
326
314
|
|
|
327
315
|
/**
|
|
@@ -722,9 +710,10 @@ class Bone {
|
|
|
722
710
|
}
|
|
723
711
|
|
|
724
712
|
if (attributes[updatedAt] && !this[updatedAt]) {
|
|
725
|
-
this[updatedAt] = this[createdAt];
|
|
713
|
+
this[updatedAt] = this[createdAt] || new Date();
|
|
726
714
|
}
|
|
727
715
|
|
|
716
|
+
const validateValues = {};
|
|
728
717
|
for (const name in attributes) {
|
|
729
718
|
const value = this.attribute(name);
|
|
730
719
|
const { defaultValue } = attributes[name];
|
|
@@ -733,10 +722,12 @@ class Bone {
|
|
|
733
722
|
} else if (value === undefined && defaultValue != null) {
|
|
734
723
|
data[name] = defaultValue;
|
|
735
724
|
}
|
|
725
|
+
if (attributes[name].primaryKey) continue;
|
|
726
|
+
validateValues[name] = data[name];
|
|
736
727
|
}
|
|
737
728
|
|
|
738
729
|
if (opts.validate !== false) {
|
|
739
|
-
this._validateAttributes();
|
|
730
|
+
this._validateAttributes(validateValues);
|
|
740
731
|
}
|
|
741
732
|
|
|
742
733
|
const spell = new Spell(Model, opts).$insert(data);
|
|
@@ -798,7 +789,7 @@ class Bone {
|
|
|
798
789
|
}, opts);
|
|
799
790
|
return result;
|
|
800
791
|
}
|
|
801
|
-
return await Model.
|
|
792
|
+
return await Model._remove(condition, forceDelete, opts);
|
|
802
793
|
}
|
|
803
794
|
|
|
804
795
|
/**
|
|
@@ -822,10 +813,10 @@ class Bone {
|
|
|
822
813
|
|
|
823
814
|
const conditions = {
|
|
824
815
|
[primaryKey]: this[primaryKey],
|
|
825
|
-
deletedAt: { $ne: null },
|
|
816
|
+
[deletedAt]: { $ne: null },
|
|
826
817
|
};
|
|
827
818
|
if (shardingKey) conditions[shardingKey] = this[shardingKey];
|
|
828
|
-
await this.update({ deletedAt: null }, { ...opts, paranoid: false });
|
|
819
|
+
await this.update({ [deletedAt]: null }, { ...opts, paranoid: false });
|
|
829
820
|
return this;
|
|
830
821
|
}
|
|
831
822
|
|
|
@@ -840,7 +831,7 @@ class Bone {
|
|
|
840
831
|
if (deletedAt == null) {
|
|
841
832
|
throw new Error('Model is not paranoid');
|
|
842
833
|
}
|
|
843
|
-
return Bone.update.call(this, conditions, { deletedAt: null }, { ...opts, paranoid: false });
|
|
834
|
+
return Bone.update.call(this, conditions, { [deletedAt]: null }, { ...opts, paranoid: false });
|
|
844
835
|
}
|
|
845
836
|
|
|
846
837
|
/**
|
|
@@ -855,9 +846,13 @@ class Bone {
|
|
|
855
846
|
const data = {};
|
|
856
847
|
const Model = this;
|
|
857
848
|
const { attributes } = Model;
|
|
858
|
-
for (const
|
|
859
|
-
|
|
860
|
-
|
|
849
|
+
for (const key in attributes) {
|
|
850
|
+
const attribute = attributes[key];
|
|
851
|
+
if (attribute.primaryKey) continue;
|
|
852
|
+
if (values[key] == null && attribute.defaultValue != null) {
|
|
853
|
+
data[key] = attribute.defaultValue;
|
|
854
|
+
} else if (values[key] !== undefined){
|
|
855
|
+
data[key] = values[key];
|
|
861
856
|
}
|
|
862
857
|
}
|
|
863
858
|
|
|
@@ -940,6 +935,15 @@ class Bone {
|
|
|
940
935
|
const attribute = new Attribute(name, attributes[name], options.define);
|
|
941
936
|
attributeMap[attribute.columnName] = attribute;
|
|
942
937
|
attributes[name] = attribute;
|
|
938
|
+
if (TIMESTAMP_NAMES.includes(name)) {
|
|
939
|
+
const { columnName } = attribute;
|
|
940
|
+
const legacyColumnName = LEGACY_TIMESTAMP_COLUMN_MAP[columnName];
|
|
941
|
+
if (!columnMap[columnName] && legacyColumnName && columnMap[legacyColumnName]) {
|
|
942
|
+
// correct columname
|
|
943
|
+
attribute.columnName = legacyColumnName;
|
|
944
|
+
attributeMap[attribute.columnName] = attribute;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
943
947
|
const columnInfo = columnMap[attribute.columnName];
|
|
944
948
|
// if datetime or timestamp precision not defined, default to column info
|
|
945
949
|
if (columnInfo && attribute.type instanceof DataTypes.DATE && attribute.type.precision == null) {
|
|
@@ -949,7 +953,7 @@ class Bone {
|
|
|
949
953
|
|
|
950
954
|
const primaryKey = Object.keys(attributes).find(key => attributes[key].primaryKey);
|
|
951
955
|
const timestamps = {};
|
|
952
|
-
for (const key of
|
|
956
|
+
for (const key of TIMESTAMP_NAMES) {
|
|
953
957
|
const name = attributes.hasOwnProperty(key) ? key : snakeCase(key);
|
|
954
958
|
const attribute = attributes[name];
|
|
955
959
|
|
|
@@ -1318,13 +1322,9 @@ class Bone {
|
|
|
1318
1322
|
static create(values, opts = {}) {
|
|
1319
1323
|
const data = Object.assign({}, values);
|
|
1320
1324
|
const instance = new this(data);
|
|
1321
|
-
if (opts.validate !== false) {
|
|
1322
|
-
instance._validateAttributes(data); // call instance._validateAttributes manually to validate the raw value
|
|
1323
|
-
}
|
|
1324
1325
|
// static create proxy to instance.create
|
|
1325
1326
|
return instance.create({
|
|
1326
1327
|
...opts,
|
|
1327
|
-
validate: false, // should not validate again
|
|
1328
1328
|
});
|
|
1329
1329
|
}
|
|
1330
1330
|
|
|
@@ -1454,6 +1454,24 @@ class Bone {
|
|
|
1454
1454
|
* @return {Spell}
|
|
1455
1455
|
*/
|
|
1456
1456
|
static remove(conditions, forceDelete = false, options) {
|
|
1457
|
+
return this._remove(conditions, forceDelete, options);
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
/**
|
|
1461
|
+
* private method for internal calling
|
|
1462
|
+
* Remove any record that matches `conditions`.
|
|
1463
|
+
* - If `forceDelete` is true, `DELETE` records from database permanently.
|
|
1464
|
+
* - If not, update `deletedAt` attribute with current date.
|
|
1465
|
+
* - If `forceDelete` isn't true and `deleteAt` isn't around, throw an Error.
|
|
1466
|
+
* @example
|
|
1467
|
+
* Post.remove({ title: 'Leah' }) // mark Post { title: 'Leah' } as deleted
|
|
1468
|
+
* Post.remove({ title: 'Leah' }, true) // delete Post { title: 'Leah' }
|
|
1469
|
+
* Post.remove({}, true) // delete all data of posts
|
|
1470
|
+
* @param {Object} conditions
|
|
1471
|
+
* @param {boolean} forceDelete
|
|
1472
|
+
* @return {Spell}
|
|
1473
|
+
*/
|
|
1474
|
+
static _remove(conditions, forceDelete = false, options) {
|
|
1457
1475
|
const { deletedAt } = this.timestamps;
|
|
1458
1476
|
if (forceDelete !== true && this.attributes[deletedAt]) {
|
|
1459
1477
|
return Bone.update.call(this, conditions, { [deletedAt]: new Date() }, {
|
package/src/collection.js
CHANGED
package/src/constants.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const AGGREGATOR_MAP = {
|
|
4
|
+
count: 'count',
|
|
5
|
+
average: 'avg',
|
|
6
|
+
minimum: 'min',
|
|
7
|
+
maximum: 'max',
|
|
8
|
+
sum: 'sum'
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const LEGACY_TIMESTAMP_MAP = {
|
|
12
|
+
gmtCreate: 'createdAt',
|
|
13
|
+
gmtModified: 'updatedAt',
|
|
14
|
+
gmtDeleted: 'deletedAt',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const LEGACY_TIMESTAMP_COLUMN_MAP = {
|
|
18
|
+
created_at: 'gmt_create',
|
|
19
|
+
updated_at: 'gmt_modified',
|
|
20
|
+
deleted_at: 'gmt_deleted',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const TIMESTAMP_NAMES = [ 'createdAt', 'updatedAt', 'deletedAt' ];
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
AGGREGATOR_MAP,
|
|
27
|
+
LEGACY_TIMESTAMP_MAP,
|
|
28
|
+
TIMESTAMP_NAMES,
|
|
29
|
+
LEGACY_TIMESTAMP_COLUMN_MAP
|
|
30
|
+
};
|
package/src/data_types.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const util = require('util');
|
|
4
4
|
const invokable = require('./utils/invokable');
|
|
5
|
+
const Raw = require('./raw');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @example
|
|
@@ -114,7 +115,7 @@ class STRING extends DataType {
|
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
uncast(value) {
|
|
117
|
-
if (value == null) return value;
|
|
118
|
+
if (value == null || value instanceof Raw) return value;
|
|
118
119
|
return '' + value;
|
|
119
120
|
}
|
|
120
121
|
}
|
|
@@ -186,15 +187,18 @@ class INTEGER extends DataType {
|
|
|
186
187
|
}
|
|
187
188
|
|
|
188
189
|
cast(value) {
|
|
189
|
-
if (value == null) return value;
|
|
190
|
+
if (value == null || isNaN(value)) return value;
|
|
190
191
|
return Number(value);
|
|
191
192
|
}
|
|
192
193
|
|
|
193
|
-
uncast(value) {
|
|
194
|
+
uncast(value, strict = true) {
|
|
194
195
|
const originValue = value;
|
|
195
|
-
if (value == null) return value;
|
|
196
|
+
if (value == null || value instanceof Raw) return value;
|
|
196
197
|
if (typeof value === 'string') value = parseInt(value, 10);
|
|
197
|
-
if (isNaN(value))
|
|
198
|
+
if (isNaN(value)) {
|
|
199
|
+
if (strict) throw new Error(util.format('invalid integer: %s', originValue));
|
|
200
|
+
return originValue;
|
|
201
|
+
}
|
|
198
202
|
return value;
|
|
199
203
|
}
|
|
200
204
|
}
|
|
@@ -243,15 +247,17 @@ class DATE extends DataType {
|
|
|
243
247
|
}
|
|
244
248
|
|
|
245
249
|
cast(value) {
|
|
250
|
+
const original = value;
|
|
246
251
|
if (value == null) return value;
|
|
247
252
|
if (!(value instanceof Date)) value = new Date(value);
|
|
253
|
+
if (isNaN(value.getTime())) return original;
|
|
248
254
|
return this._round(value);
|
|
249
255
|
}
|
|
250
256
|
|
|
251
257
|
uncast(value) {
|
|
252
258
|
const originValue = value;
|
|
253
259
|
|
|
254
|
-
if (value == null) return value;
|
|
260
|
+
if (value == null || value instanceof Raw) return value;
|
|
255
261
|
if (typeof value.toDate === 'function') {
|
|
256
262
|
value = value.toDate();
|
|
257
263
|
}
|
|
@@ -289,15 +295,17 @@ class DATEONLY extends DataType {
|
|
|
289
295
|
}
|
|
290
296
|
|
|
291
297
|
cast(value) {
|
|
298
|
+
const original = value;
|
|
292
299
|
if (value == null) return value;
|
|
293
300
|
if (!(value instanceof Date)) value = new Date(value);
|
|
301
|
+
if (isNaN(value.getTime())) return original;
|
|
294
302
|
return this._round(value);
|
|
295
303
|
}
|
|
296
304
|
|
|
297
305
|
uncast(value) {
|
|
298
306
|
const originValue = value;
|
|
299
307
|
|
|
300
|
-
if (value == null) return value;
|
|
308
|
+
if (value == null || value instanceof Raw) return value;
|
|
301
309
|
if (typeof value.toDate === 'function') {
|
|
302
310
|
value = value.toDate();
|
|
303
311
|
}
|
|
@@ -309,7 +317,7 @@ class DATEONLY extends DataType {
|
|
|
309
317
|
}
|
|
310
318
|
|
|
311
319
|
if (!(value instanceof Date)) value = new Date(value);
|
|
312
|
-
if (isNaN(value)) throw new Error(util.format('invalid date: %s', originValue))
|
|
320
|
+
if (isNaN(value)) throw new Error(util.format('invalid date: %s', originValue));
|
|
313
321
|
|
|
314
322
|
return this._round(value);
|
|
315
323
|
}
|
|
@@ -393,7 +401,7 @@ class JSON extends DataType {
|
|
|
393
401
|
}
|
|
394
402
|
|
|
395
403
|
uncast(value) {
|
|
396
|
-
if (value == null) return value;
|
|
404
|
+
if (value == null || value instanceof Raw) return value;
|
|
397
405
|
return global.JSON.stringify(value);
|
|
398
406
|
}
|
|
399
407
|
}
|
|
@@ -126,11 +126,11 @@ class Attribute {
|
|
|
126
126
|
return this.type.cast(value);
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
uncast(value) {
|
|
129
|
+
uncast(value, strict = true) {
|
|
130
130
|
if (Array.isArray(value) && this.jsType !== JSON) {
|
|
131
|
-
return value.map(entry => this.type.uncast(entry));
|
|
131
|
+
return value.map(entry => this.type.uncast(entry, strict));
|
|
132
132
|
}
|
|
133
|
-
return this.type.uncast(value);
|
|
133
|
+
return this.type.uncast(value, strict);
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
@@ -129,7 +129,7 @@ function createSubspell(spell) {
|
|
|
129
129
|
subspell.orders = [];
|
|
130
130
|
for (const order of orders) {
|
|
131
131
|
const [token, direction] = order;
|
|
132
|
-
const { type, qualifiers, value } = token;
|
|
132
|
+
const { type, qualifiers = [], value } = token;
|
|
133
133
|
if (type == 'id' && qualifiers[0] == baseName) {
|
|
134
134
|
subspell.orders.push([{ type, value }, direction]);
|
|
135
135
|
}
|
|
@@ -220,7 +220,7 @@ function formatSelectWithJoin(spell) {
|
|
|
220
220
|
|
|
221
221
|
let hoistable = skip > 0 || rowCount > 0;
|
|
222
222
|
if (hoistable) {
|
|
223
|
-
function checkQualifier({ type, qualifiers }) {
|
|
223
|
+
function checkQualifier({ type, qualifiers = [] }) {
|
|
224
224
|
if (type === 'id' && qualifiers.length> 0 && !qualifiers.includes(baseName)) {
|
|
225
225
|
hoistable = false;
|
|
226
226
|
}
|
|
@@ -476,6 +476,7 @@ function formatUpdate(spell) {
|
|
|
476
476
|
for (const condition of whereConditions) collectLiteral(spell, condition, values);
|
|
477
477
|
chunks.push(`WHERE ${formatConditions(spell, whereConditions)}`);
|
|
478
478
|
}
|
|
479
|
+
|
|
479
480
|
return {
|
|
480
481
|
sql: chunks.join(' '),
|
|
481
482
|
values,
|
|
@@ -606,4 +607,5 @@ module.exports = {
|
|
|
606
607
|
formatSelectWithoutJoin,
|
|
607
608
|
formatUpdateOnDuplicate,
|
|
608
609
|
formatReturning,
|
|
610
|
+
formatOrders
|
|
609
611
|
};
|
|
@@ -58,4 +58,36 @@ module.exports = {
|
|
|
58
58
|
formatReturning() {
|
|
59
59
|
return '';
|
|
60
60
|
},
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* UPDATE ... ORDER BY ... LIMIT ${rowCount}
|
|
64
|
+
* - https://dev.mysql.com/doc/refman/8.0/en/update.html
|
|
65
|
+
* @param {Spell} spell
|
|
66
|
+
*/
|
|
67
|
+
formatUpdate(spell) {
|
|
68
|
+
const result = spellbook.formatUpdate.call(this, spell);
|
|
69
|
+
const { rowCount, orders } = spell;
|
|
70
|
+
const chunks = [];
|
|
71
|
+
|
|
72
|
+
if (orders.length > 0) chunks.push(`ORDER BY ${this.formatOrders(spell, orders).join(', ')}`);
|
|
73
|
+
if (rowCount > 0) chunks.push(`LIMIT ${rowCount}`);
|
|
74
|
+
if (chunks.length > 0) result.sql += ` ${chunks.join(' ')}`;
|
|
75
|
+
|
|
76
|
+
return result;
|
|
77
|
+
},
|
|
78
|
+
/**
|
|
79
|
+
* DELETE ... ORDER BY ...LIMIT
|
|
80
|
+
* @param {Spell} spell
|
|
81
|
+
*/
|
|
82
|
+
formatDelete(spell) {
|
|
83
|
+
const result = spellbook.formatDelete.call(this, spell);
|
|
84
|
+
const { rowCount, orders } = spell;
|
|
85
|
+
const chunks = [];
|
|
86
|
+
|
|
87
|
+
if (orders.length > 0) chunks.push(`ORDER BY ${this.formatOrders(spell, orders).join(', ')}`);
|
|
88
|
+
if (rowCount > 0) chunks.push(`LIMIT ${rowCount}`);
|
|
89
|
+
if (chunks.length > 0) result.sql += ` ${chunks.join(' ')}`;
|
|
90
|
+
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
61
93
|
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const DataTypes = require('../../data_types');
|
|
4
|
+
const util = require('util');
|
|
5
|
+
const Raw = require('../../raw');
|
|
6
|
+
|
|
4
7
|
|
|
5
8
|
class Postgres_DATE extends DataTypes.DATE {
|
|
6
9
|
constructor(precision, timezone = true) {
|
|
@@ -37,12 +40,35 @@ class Postgres_BINARY extends DataTypes {
|
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
|
|
43
|
+
class Postgres_INTEGER extends DataTypes.INTEGER {
|
|
44
|
+
constructor(length) {
|
|
45
|
+
super(length);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
uncast(value) {
|
|
49
|
+
const originValue = value;
|
|
50
|
+
if (value == null || value instanceof Raw) return value;
|
|
51
|
+
if (typeof value === 'string') value = parseInt(value, 10);
|
|
52
|
+
if (isNaN(value)) throw new Error(util.format('invalid integer: %s', originValue));
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
class Postgres_BIGINT extends Postgres_INTEGER {
|
|
58
|
+
constructor() {
|
|
59
|
+
super();
|
|
60
|
+
this.dataType = 'bigint';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
40
64
|
class Postgres_DataTypes extends DataTypes {
|
|
41
65
|
static DATE = Postgres_DATE;
|
|
42
66
|
static JSONB = Postgres_JSONB;
|
|
43
67
|
static BINARY = Postgres_BINARY;
|
|
44
68
|
static VARBINARY = Postgres_BINARY;
|
|
45
69
|
static BLOB = Postgres_BINARY;
|
|
70
|
+
static INTEGER = Postgres_INTEGER;
|
|
71
|
+
static BIGINT = Postgres_BIGINT;
|
|
46
72
|
}
|
|
47
73
|
|
|
48
74
|
module.exports = Postgres_DataTypes;
|
|
@@ -57,13 +57,18 @@ module.exports = {
|
|
|
57
57
|
|
|
58
58
|
if (dataType === 'character varying') dataType = 'varchar';
|
|
59
59
|
if (dataType === 'timestamp without time zone') dataType = 'timestamp';
|
|
60
|
+
|
|
61
|
+
let columnDefault = row.column_default;
|
|
62
|
+
if (/^NULL::/i.test(columnDefault)) columnDefault = null;
|
|
63
|
+
if (dataType === 'boolean') columnDefault = columnDefault === 'true';
|
|
64
|
+
|
|
60
65
|
const primaryKey = row.constraint_type === 'PRIMARY KEY';
|
|
61
66
|
const precision = row.datetime_precision;
|
|
62
67
|
|
|
63
68
|
columns.push({
|
|
64
69
|
columnName: row.column_name,
|
|
65
70
|
columnType: length > 0 ? `${dataType}(${length})` : dataType,
|
|
66
|
-
defaultValue: primaryKey ? null :
|
|
71
|
+
defaultValue: primaryKey ? null : columnDefault,
|
|
67
72
|
dataType,
|
|
68
73
|
allowNull: row.is_nullable !== 'NO',
|
|
69
74
|
// https://www.postgresql.org/docs/9.5/infoschema-table-constraints.html
|
|
@@ -7,8 +7,42 @@ class Sqlite_DATE extends DataTypes.DATE {
|
|
|
7
7
|
super(precision, timezone);
|
|
8
8
|
this.dataType = 'datetime';
|
|
9
9
|
}
|
|
10
|
+
|
|
11
|
+
uncast(value) {
|
|
12
|
+
try {
|
|
13
|
+
return super.uncast(value);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.error(new Error(`unable to cast ${value} to DATE`));
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class Sqlite_DATEONLY extends DataTypes.DATEONLY {
|
|
22
|
+
constructor() {
|
|
23
|
+
super();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
uncast(value) {
|
|
27
|
+
try {
|
|
28
|
+
return super.uncast(value);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error(new Error(`unable to cast ${value} to DATEONLY`));
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
10
34
|
}
|
|
11
35
|
|
|
36
|
+
class Sqlite_INTEGER extends DataTypes.INTEGER {
|
|
37
|
+
constructor(length) {
|
|
38
|
+
super(length);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
uncast(value) {
|
|
42
|
+
return super.uncast(value, false);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
12
46
|
class Sqlite_BIGINT extends DataTypes.BIGINT {
|
|
13
47
|
constructor() {
|
|
14
48
|
super();
|
|
@@ -58,6 +92,14 @@ class Sqlite_DataTypes extends DataTypes {
|
|
|
58
92
|
static get VARBINARY() {
|
|
59
93
|
return Sqlite_VARBINARY;
|
|
60
94
|
}
|
|
95
|
+
|
|
96
|
+
static get DATEONLY() {
|
|
97
|
+
return Sqlite_DATEONLY;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static get INTEGER() {
|
|
101
|
+
return Sqlite_INTEGER;
|
|
102
|
+
}
|
|
61
103
|
}
|
|
62
104
|
|
|
63
105
|
module.exports = Sqlite_DataTypes;
|
|
@@ -75,8 +75,9 @@ async function alterTableWithChangeColumn(driver, table, changes) {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
// eslint-disable-next-line no-unused-vars
|
|
78
|
-
function parseDefaultValue(text) {
|
|
78
|
+
function parseDefaultValue(text, type) {
|
|
79
79
|
if (typeof text !== 'string') return text;
|
|
80
|
+
if (type === 'boolean') return text === 'true';
|
|
80
81
|
|
|
81
82
|
try {
|
|
82
83
|
const ast = parseExpr(text);
|
|
@@ -84,7 +85,7 @@ function parseDefaultValue(text) {
|
|
|
84
85
|
return ast.value;
|
|
85
86
|
}
|
|
86
87
|
} catch (err) {
|
|
87
|
-
debug('[parseDefaultValue] [%s] %s', text, err
|
|
88
|
+
debug('[parseDefaultValue] [%s] %s', text, err);
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
return text;
|
|
@@ -111,10 +112,11 @@ module.exports = {
|
|
|
111
112
|
const columnType = type.toLowerCase();
|
|
112
113
|
const [, dataType, precision ] = columnType.match(rColumnType);
|
|
113
114
|
const primaryKey = pk === 1;
|
|
115
|
+
|
|
114
116
|
const result = {
|
|
115
117
|
columnName: name,
|
|
116
118
|
columnType,
|
|
117
|
-
defaultValue: parseDefaultValue(dflt_value),
|
|
119
|
+
defaultValue: parseDefaultValue(dflt_value, type),
|
|
118
120
|
dataType: dataType,
|
|
119
121
|
allowNull: primaryKey ? false : notnull == 0,
|
|
120
122
|
primaryKey,
|
package/src/expr_formatter.js
CHANGED
|
@@ -208,7 +208,7 @@ function coerceLiteral(spell, ast) {
|
|
|
208
208
|
if (arg.type === 'literal') {
|
|
209
209
|
// { params: { $like: '%foo%' } }
|
|
210
210
|
if (attribute.jsType === JSON && typeof arg.value === 'string') continue;
|
|
211
|
-
arg.value = attribute.uncast(arg.value);
|
|
211
|
+
arg.value = attribute.uncast(arg.value, false);
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
}
|
package/src/realm.js
CHANGED
|
@@ -8,6 +8,7 @@ const { findDriver } = require('./drivers');
|
|
|
8
8
|
const { camelCase } = require('./utils/string');
|
|
9
9
|
const sequelize = require('./adapters/sequelize');
|
|
10
10
|
const Raw = require('./raw');
|
|
11
|
+
const { LEGACY_TIMESTAMP_MAP } = require('./constants');
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
*
|
|
@@ -39,12 +40,6 @@ async function findModels(dir) {
|
|
|
39
40
|
return models;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
const LEGACY_TIMESTAMP_MAP = {
|
|
43
|
-
gmtCreate: 'createdAt',
|
|
44
|
-
gmtModified: 'updatedAt',
|
|
45
|
-
gmtDeleted: 'deletedAt',
|
|
46
|
-
};
|
|
47
|
-
|
|
48
43
|
/**
|
|
49
44
|
* construct model attributes entirely from column definitions
|
|
50
45
|
* @param {Bone} model
|
|
@@ -54,9 +49,8 @@ function initAttributes(model, columns) {
|
|
|
54
49
|
const attributes = {};
|
|
55
50
|
|
|
56
51
|
for (const columnInfo of columns) {
|
|
57
|
-
const { columnName, columnType,
|
|
52
|
+
const { columnName, columnType, ...restInfo } = columnInfo;
|
|
58
53
|
const name = columnName === '_id' ? columnName : camelCase(columnName);
|
|
59
|
-
// leave out defaultValue to let database take over the default
|
|
60
54
|
attributes[name] = {
|
|
61
55
|
...restInfo,
|
|
62
56
|
columnName,
|
package/src/spell.js
CHANGED
|
@@ -11,7 +11,7 @@ const { isPlainObject } = require('./utils');
|
|
|
11
11
|
const { IndexHint, INDEX_HINT_TYPE, Hint } = require('./hint');
|
|
12
12
|
const { parseObject } = require('./query_object');
|
|
13
13
|
const Raw = require('./raw');
|
|
14
|
-
const { AGGREGATOR_MAP } = require('./
|
|
14
|
+
const { AGGREGATOR_MAP } = require('./constants');
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Parse condition expressions
|
package/History.md
DELETED
|
@@ -1,550 +0,0 @@
|
|
|
1
|
-
2.0.1 / 2022-01-05
|
|
2
|
-
==================
|
|
3
|
-
|
|
4
|
-
## What's Changed
|
|
5
|
-
* fix: format numeric result by @JimmyDaddy in https://github.com/cyjake/leoric/pull/253
|
|
6
|
-
* fix: should still return number if value is '0.000' by @cyjake in https://github.com/cyjake/leoric/pull/254
|
|
7
|
-
|
|
8
|
-
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.15.1...v2.0.1
|
|
9
|
-
|
|
10
|
-
2.0.0 / 2021-12-28
|
|
11
|
-
==================
|
|
12
|
-
|
|
13
|
-
## What's Changed
|
|
14
|
-
* breaking: model.sync add force/alter option by @SmartOrange in https://github.com/cyjake/leoric/pull/224
|
|
15
|
-
* breaking: logQueryError(err, sql, duration, options) by @cyjake in https://github.com/cyjake/leoric/pull/237
|
|
16
|
-
* test: add utf8mb4 test cases by @fengmk2 in https://github.com/cyjake/leoric/pull/239
|
|
17
|
-
* Merge 1.x changes by @cyjake in https://github.com/cyjake/leoric/pull/249
|
|
18
|
-
|
|
19
|
-
## New Contributors
|
|
20
|
-
* @SmartOrange made their first contribution in https://github.com/cyjake/leoric/pull/222
|
|
21
|
-
|
|
22
|
-
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.15.1...v2.0.0
|
|
23
|
-
|
|
24
|
-
1.15.1 / 2021-12-28
|
|
25
|
-
===================
|
|
26
|
-
|
|
27
|
-
## What's Changed
|
|
28
|
-
* fix: fix #242 date string format by @JimmyDaddy in https://github.com/cyjake/leoric/pull/243
|
|
29
|
-
* fix: update with empty conditions by @JimmyDaddy in https://github.com/cyjake/leoric/pull/241
|
|
30
|
-
* fix: silent option's priority should be lower than valueSet by @JimmyDaddy in https://github.com/cyjake/leoric/pull/244
|
|
31
|
-
* fix: information_schema.columns.datetime_precision by @cyjake in https://github.com/cyjake/leoric/pull/246
|
|
32
|
-
* fix: should not hoist subquery if query is ordered by external columns by @cyjake in https://github.com/cyjake/leoric/pull/247
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.15.0...v1.15.1
|
|
36
|
-
|
|
37
|
-
1.15.0 / 2021-11-22
|
|
38
|
-
===================
|
|
39
|
-
|
|
40
|
-
## What's Changed
|
|
41
|
-
* feat: make duration in precise milliseconds by @fengmk2 in https://github.com/cyjake/leoric/pull/236
|
|
42
|
-
* fix: spell.increment() & spell.decrement() @cyjake https://github.com/cyjake/leoric/pull/234
|
|
43
|
-
* fix: bulkCreate should adapte empty data @JimmyDaddy https://github.com/cyjake/leoric/pull/232
|
|
44
|
-
|
|
45
|
-
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.14.4...v1.14.5
|
|
46
|
-
|
|
47
|
-
1.14.4 / 2021-11-15
|
|
48
|
-
===================
|
|
49
|
-
|
|
50
|
-
## What's Changed
|
|
51
|
-
|
|
52
|
-
* test: PostgreSQL v14 test case compatibility by @cyjake https://github.com/cyjake/leoric/pull/230
|
|
53
|
-
* fix: turn off subquery optimization if query criteria contains other column by @cyjake https://github.com/cyjake/leoric/pull/229
|
|
54
|
-
* fix: bone.changed() return `false | string[]` type by @fengmk2 https://github.com/cyjake/leoric/pull/231
|
|
55
|
-
|
|
56
|
-
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.14.3...v1.14.4
|
|
57
|
-
|
|
58
|
-
1.14.3 / 2021-11-12
|
|
59
|
-
===================
|
|
60
|
-
|
|
61
|
-
## What's Changed
|
|
62
|
-
* fix: logger.logQuery should be guarded in case of error by @SmartOrange in https://github.com/cyjake/leoric/pull/222
|
|
63
|
-
* fix: findOne without result should return null by @JimmyDaddy in https://github.com/cyjake/leoric/pull/225
|
|
64
|
-
* fix: Literal should support bigint type by @fengmk2 in https://github.com/cyjake/leoric/pull/226
|
|
65
|
-
* fix: select((name: string) => boolean) by @cyjake in https://github.com/cyjake/leoric/pull/227
|
|
66
|
-
|
|
67
|
-
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.14.2...v1.14.3
|
|
68
|
-
|
|
69
|
-
1.14.2 / 2021-11-01
|
|
70
|
-
===================
|
|
71
|
-
|
|
72
|
-
## What's Changed
|
|
73
|
-
* fix: accept timestamps in snake case by @cyjake in https://github.com/cyjake/leoric/pull/221
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.14.1...v1.14.2
|
|
77
|
-
|
|
78
|
-
1.14.1 / 2021-11-01
|
|
79
|
-
===================
|
|
80
|
-
|
|
81
|
-
## What's Changed
|
|
82
|
-
* docs: export { Collection } by @cyjake in https://github.com/cyjake/leoric/pull/220
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.14.0...v1.14.1
|
|
86
|
-
|
|
87
|
-
1.14.0 / 2021-11-01
|
|
88
|
-
===================
|
|
89
|
-
|
|
90
|
-
Two options regarding `Model.init()` were added in this release:
|
|
91
|
-
|
|
92
|
-
```js
|
|
93
|
-
class User extends Bone {}
|
|
94
|
-
User.init({ name: STRING }, {
|
|
95
|
-
timestamps: true, // which is the default
|
|
96
|
-
paranoid: true, // which default to `false`
|
|
97
|
-
});
|
|
98
|
-
assert.deepEqual(Object.keys(User.attributes), [
|
|
99
|
-
'id',
|
|
100
|
-
'name',
|
|
101
|
-
'createdAt',
|
|
102
|
-
'updatedAt',
|
|
103
|
-
'deletedAt',
|
|
104
|
-
]);
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
## What's Changed
|
|
108
|
-
* docs: update 'primayKey' typos by @freshgum-bubbles in https://github.com/cyjake/leoric/pull/211
|
|
109
|
-
* docs: DataTypes definitions in d.ts by @cyjake in https://github.com/cyjake/leoric/pull/210
|
|
110
|
-
* fix: fix#209 sequelize mode should update all changed fields in instance update method by @JimmyDaddy in https://github.com/cyjake/leoric/pull/212
|
|
111
|
-
* fix: fix #213 findAndCountAll should ignore attributes by @JimmyDaddy in https://github.com/cyjake/leoric/pull/214
|
|
112
|
-
* fix: opts.connectTimeout by @cyjake in https://github.com/cyjake/leoric/pull/216
|
|
113
|
-
* fix: reload instance with sharding key should not throw by @cyjake in https://github.com/cyjake/leoric/pull/217
|
|
114
|
-
* feat: timestamps should be defined by default by @cyjake in https://github.com/cyjake/leoric/pull/218
|
|
115
|
-
* fix: instance.reload() should not rely on `static findOne()` by @cyjake in https://github.com/cyjake/leoric/pull/219
|
|
116
|
-
|
|
117
|
-
## New Contributors
|
|
118
|
-
* @freshgum-bubbles made their first contribution in https://github.com/cyjake/leoric/pull/211
|
|
119
|
-
|
|
120
|
-
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.13.5...v1.14.0
|
|
121
|
-
|
|
122
|
-
1.13.5 / 2021-10-26
|
|
123
|
-
===================
|
|
124
|
-
|
|
125
|
-
## What's Changed
|
|
126
|
-
* docs: enhance aggregation query types & fix raw query result type by @cyjake in https://github.com/cyjake/leoric/pull/208
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.13.4...v1.13.5
|
|
130
|
-
|
|
131
|
-
1.13.4 / 2021-10-25
|
|
132
|
-
===================
|
|
133
|
-
|
|
134
|
-
## What's Changed
|
|
135
|
-
* docs: spell & model methods should be generic by @cyjake in https://github.com/cyjake/leoric/pull/206
|
|
136
|
-
* docs: enhance query options, instance type, and toJSON() result type by @cyjake in https://github.com/cyjake/leoric/pull/207
|
|
137
|
-
|
|
138
|
-
This version brings correct (and hopefully better) typescript definitions, with the dts checked continuously at test/types tests. With this version, users that have model types correctly pinned at Bone will get code completion including class fields. Such as:
|
|
139
|
-
|
|
140
|
-

|
|
141
|
-
|
|
142
|
-
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.13.3...v1.13.4
|
|
143
|
-
|
|
144
|
-
1.13.3 / 2021-10-21
|
|
145
|
-
===================
|
|
146
|
-
|
|
147
|
-
## What's Changed
|
|
148
|
-
* refactor: persist edge cases of type casting in integration tests by @cyjake in https://github.com/cyjake/leoric/pull/202
|
|
149
|
-
* docs: renaming attributes by @cyjake in https://github.com/cyjake/leoric/pull/203
|
|
150
|
-
* fix: JSON.uncast(string) should not serialize twice by @cyjake in https://github.com/cyjake/leoric/pull/205
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.13.2...v1.13.3
|
|
154
|
-
|
|
155
|
-
1.13.2 / 2021-10-18
|
|
156
|
-
===================
|
|
157
|
-
|
|
158
|
-
## What's Changed
|
|
159
|
-
* fix: attribute.uncast([]) and realm.connect with synchronized models by @cyjake in https://github.com/cyjake/leoric/pull/201
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.13.1...v1.13.2
|
|
163
|
-
|
|
164
|
-
1.13.1 / 2021-10-18
|
|
165
|
-
===================
|
|
166
|
-
|
|
167
|
-
## What's Changed
|
|
168
|
-
* fix: skip connecting if models are synchronized already by @cyjake in https://github.com/cyjake/leoric/pull/200
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.13.0...v1.13.1
|
|
172
|
-
|
|
173
|
-
1.13.0 / 2021-10-18
|
|
174
|
-
===================
|
|
175
|
-
|
|
176
|
-
## What's Changed
|
|
177
|
-
* docs: monthly updates of 2021.09; support dark mode by @cyjake in https://github.com/cyjake/leoric/pull/196
|
|
178
|
-
* feat: coerce literal values into accurate attribute type by @cyjake in https://github.com/cyjake/leoric/pull/197
|
|
179
|
-
* fix: dispatched result should be in attribute names by @cyjake in https://github.com/cyjake/leoric/pull/198
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.12.0...v1.13.0
|
|
183
|
-
|
|
184
|
-
1.12.0 / 2021-10-12
|
|
185
|
-
===================
|
|
186
|
-
|
|
187
|
-
* feat: support custom fields query and sequelize mode export rawAttributes (#192)
|
|
188
|
-
* refactor: collection format query result (#194)
|
|
189
|
-
* refactor: object condition parsing and expression formatting (#191)
|
|
190
|
-
|
|
191
|
-
1.11.1 / 2021-09-28
|
|
192
|
-
===================
|
|
193
|
-
|
|
194
|
-
This version fixes lots of issues regarding logical operator in object conditions.
|
|
195
|
-
|
|
196
|
-
* fix: logical operator with multiple conditions such as (#190)
|
|
197
|
-
* fix: sequelize mode support HAVING, and select fields raw sql support (#187)
|
|
198
|
-
* fix: support len validator (#188)
|
|
199
|
-
* fix: normalize logical operator conditions before formatting with spellbook (#186)
|
|
200
|
-
|
|
201
|
-
1.11.0 / 2021-09-24
|
|
202
|
-
===================
|
|
203
|
-
|
|
204
|
-
* feat: support BINARY(length), VARBINARY(length), and BLOB (#169)
|
|
205
|
-
* fix: logic operate should adapt one argument (#183)
|
|
206
|
-
* fix: Bone.load() should be idempotent, make sure associations is intact (#184)
|
|
207
|
-
* fix: selected instance isNewRecord is false (#182)
|
|
208
|
-
* fix: set options.busyTimeout to mitigate SQLITE_BUSY (#176)
|
|
209
|
-
* fix: turn on long stack trace of sqlite driver (#175)
|
|
210
|
-
* docs: how to contribute (#180)
|
|
211
|
-
|
|
212
|
-
1.10.0 / 2021-09-14
|
|
213
|
-
===================
|
|
214
|
-
|
|
215
|
-
* feat: SQLite driver should emit "connection" event when new connection is created (#168)
|
|
216
|
-
* fix: bulkCreate(...records) should recognize custom setters (#168)
|
|
217
|
-
* fix: attribute.equals() check should ignore defaultValue (#172)
|
|
218
|
-
|
|
219
|
-
1.9.0 / 2021-09-04
|
|
220
|
-
==================
|
|
221
|
-
|
|
222
|
-
> should've been a major release but since existing users have all migrated to the new api...
|
|
223
|
-
|
|
224
|
-
* breaking: drop the deprecated `Model.describe()` (#167)
|
|
225
|
-
|
|
226
|
-
1.8.0 / 2021-08-30
|
|
227
|
-
==================
|
|
228
|
-
|
|
229
|
-
* feat: silent option fix #164 (#165)
|
|
230
|
-
* feat: binary type (#166)
|
|
231
|
-
|
|
232
|
-
1.7.1 / 2021-08-17
|
|
233
|
-
==================
|
|
234
|
-
|
|
235
|
-
* revert: drop Driver#recycleConnections due to poor interoperability (#162)
|
|
236
|
-
* fix: validator call array arguments (#160)
|
|
237
|
-
|
|
238
|
-
1.7.0 / 2021-08-17
|
|
239
|
-
=================
|
|
240
|
-
|
|
241
|
-
* feat: close connections that exceeds opts.idleTimeout (#159)
|
|
242
|
-
* feat: `opts.connectionLimit` support for SQLite (#159)
|
|
243
|
-
* feat: raw query relpacements, closes #149 (#155)
|
|
244
|
-
* fix: upsert created_at default (#154)
|
|
245
|
-
* test: validator unit test (#157)
|
|
246
|
-
* test: setup_hooks unit test (#158)
|
|
247
|
-
|
|
248
|
-
1.6.7 / 2021-08-05
|
|
249
|
-
==================
|
|
250
|
-
|
|
251
|
-
* fix: prevent from calling Date.prototype.toJSON (#153)
|
|
252
|
-
|
|
253
|
-
1.6.6 / 2021-07-22
|
|
254
|
-
==================
|
|
255
|
-
|
|
256
|
-
* fix: subclassing data type in dialects (#145)
|
|
257
|
-
* fix: where('width / height >= 16 / 9') (#144)
|
|
258
|
-
* docs: logging and sequelzie adapter (zh) (#142)
|
|
259
|
-
* test: include test/unit/utils (#143)
|
|
260
|
-
* test: more tests cases about sequelize adapter (#141)
|
|
261
|
-
|
|
262
|
-
1.6.5 / 2021-07-16
|
|
263
|
-
==================
|
|
264
|
-
|
|
265
|
-
* fix: define assign Bone.models #140
|
|
266
|
-
|
|
267
|
-
1.6.4 / 2021-07-16
|
|
268
|
-
==================
|
|
269
|
-
|
|
270
|
-
* refactor: connect({ Bone }) still necessary (#139)
|
|
271
|
-
* fix: formatting select join with subqueries should not tamper the subquery itself (#138)
|
|
272
|
-
* fix: describe table with more compatible syntax (#137)
|
|
273
|
-
|
|
274
|
-
1.6.3 / 2021-07-14
|
|
275
|
-
==================
|
|
276
|
-
|
|
277
|
-
* fix: transaction option passing in sequelize adapter (#136)
|
|
278
|
-
* fix: this.Model and proper Model.describe() (#135)
|
|
279
|
-
|
|
280
|
-
1.6.2 / 2021-07-09
|
|
281
|
-
==================
|
|
282
|
-
|
|
283
|
-
* fix: convert datetime in seconds/milliseconds back to Date (#134)
|
|
284
|
-
* fix: renamed attribute should remain enumerable (#133)
|
|
285
|
-
|
|
286
|
-
1.6.1 / 2021-07-07
|
|
287
|
-
==================
|
|
288
|
-
|
|
289
|
-
* fix: collection convert should handle tddl results as well (#132)
|
|
290
|
-
|
|
291
|
-
1.6.0 / 2021-07-06
|
|
292
|
-
==================
|
|
293
|
-
|
|
294
|
-
* feat: support class static attributes and hooks (#131)
|
|
295
|
-
* fix: names defined in Bone.attributes should always be enumerable (#128)
|
|
296
|
-
* chore: add quality badge to readme (#129)
|
|
297
|
-
|
|
298
|
-
1.5.2 / 2021-07-02
|
|
299
|
-
==================
|
|
300
|
-
|
|
301
|
-
* fix: leave the getter properties defined in class syntax as is (#127)
|
|
302
|
-
|
|
303
|
-
1.5.1 / 2021-06-30
|
|
304
|
-
==================
|
|
305
|
-
|
|
306
|
-
* fix: export Logger and Spell to let users intercept lower level api calls (#126)
|
|
307
|
-
|
|
308
|
-
1.5.0 / 2021-06-30
|
|
309
|
-
==================
|
|
310
|
-
|
|
311
|
-
* feat: provide Bone.pool to be backward compatible with v0.x (#124)
|
|
312
|
-
* feat: complete bone/spine.restore and Bone API type definitions (#125)
|
|
313
|
-
* feat: support more data types (mediumtext, mediumint, char, date...) (#123)
|
|
314
|
-
|
|
315
|
-
1.4.1 / 2021-06-25
|
|
316
|
-
==================
|
|
317
|
-
|
|
318
|
-
* refactor: simplify legacy timestamps support (#120)
|
|
319
|
-
* refactor: do not subclass Bone unless asked specifically (#120)
|
|
320
|
-
|
|
321
|
-
1.4.0 / 2021-06-24
|
|
322
|
-
==================
|
|
323
|
-
|
|
324
|
-
* feat: `realm.raw('SELECT ...')` and `Model.raw('SELECT ...')` (#94)
|
|
325
|
-
* feat: support multiple order rules in one single string or one-dimensional array (#92)
|
|
326
|
-
* feat: `Model.truncate()` now uses TRUNCATE if possible
|
|
327
|
-
* feat: `Model.find().optimizerHints('SET_VAR(foreign_key_checks=OFF)')`
|
|
328
|
-
* fix: Bone.bulkCreate() should not throw when called with non attribute (#117)
|
|
329
|
-
* fix: batch upsert (#108)
|
|
330
|
-
* fix: make sure connection is passed around in all queries carried out within transaction (#105)
|
|
331
|
-
* fix: update, sequelize mode get API, destroy compitable (#104)
|
|
332
|
-
* fix: `setDataValue` in sequelize adapter should not check prop name strictly
|
|
333
|
-
* refactor: spell_insert (#118)
|
|
334
|
-
* docs: about egg-orm & migrations (#119)
|
|
335
|
-
* docs: revise instructions for installing Jekyll (#111)
|
|
336
|
-
* docs: migrations, validations, hooks, and sequelize adapter (#103)
|
|
337
|
-
* docs: contributing guides
|
|
338
|
-
|
|
339
|
-
1.3.0 / 2021-03-01
|
|
340
|
-
==================
|
|
341
|
-
|
|
342
|
-
* feat: hook support
|
|
343
|
-
* feat: dirty check (`changes()` & `previousChanges()`)
|
|
344
|
-
* feat: compatible with mysql longtext conversion
|
|
345
|
-
* feat: NOT condition
|
|
346
|
-
|
|
347
|
-
1.2.0 / 2020-12-10
|
|
348
|
-
==================
|
|
349
|
-
|
|
350
|
-
* feat: `Realm.prototype.transaction()` with async function support
|
|
351
|
-
* feat: `Realm.prototype.query()` for raw queries
|
|
352
|
-
* feat: `logger.logQuery(sql, duration, { Model, command })`
|
|
353
|
-
* feat: `logger.logQueryError(sql, err, duration, { Model, command })`
|
|
354
|
-
|
|
355
|
-
1.1.0 / 2020-11-23
|
|
356
|
-
==================
|
|
357
|
-
|
|
358
|
-
* feat: JSON and JSONB data types
|
|
359
|
-
* feat: support `stringifyObjects` option for mysql client
|
|
360
|
-
* feat: aggregate functions for sequelize adapter
|
|
361
|
-
* feat: `Spell.prototype.nodeify()`
|
|
362
|
-
|
|
363
|
-
1.0.3 / 2020-03-16
|
|
364
|
-
==================
|
|
365
|
-
|
|
366
|
-
* fix: replace `deep-equal` (which is bloated) with `util.isDeepStrictEqual`
|
|
367
|
-
|
|
368
|
-
1.0.2 / 2020-03-04
|
|
369
|
-
==================
|
|
370
|
-
|
|
371
|
-
* fix: driver.alterTable() with multiple columns to add in SQLite
|
|
372
|
-
|
|
373
|
-
1.0.1 / 2020-02-25
|
|
374
|
-
==================
|
|
375
|
-
|
|
376
|
-
* fix: bulkCreate in sequelize shim
|
|
377
|
-
|
|
378
|
-
1.0.0 / 2020-02-24
|
|
379
|
-
==================
|
|
380
|
-
|
|
381
|
-
First major release. Let's get serious with semver.
|
|
382
|
-
|
|
383
|
-
* feat: logger.logQuery(sql, duration) & logger.logQueryError(sql, err)
|
|
384
|
-
|
|
385
|
-
0.5.3 / 2020-02-22
|
|
386
|
-
==================
|
|
387
|
-
|
|
388
|
-
* fix: `connect({ sequelize, dialect, client })` to allow mandatory sqlite client
|
|
389
|
-
* fix: prevent queries being performed unless model is correctly connected
|
|
390
|
-
|
|
391
|
-
0.5.2 / 2020-02-21
|
|
392
|
-
==================
|
|
393
|
-
|
|
394
|
-
* fix: drop the default and unused `require('sqlite3')`
|
|
395
|
-
|
|
396
|
-
0.5.1 / 2020-02-21
|
|
397
|
-
==================
|
|
398
|
-
|
|
399
|
-
* fix: `connect({ client: '@journeyapps/sqlcipher' })`
|
|
400
|
-
|
|
401
|
-
0.5.0 / 2020-02-19
|
|
402
|
-
==================
|
|
403
|
-
|
|
404
|
-
* feat: `Bone.sync()` to synchronize model with database
|
|
405
|
-
* feat: `Bone.createMigrationFile()` to create migration file
|
|
406
|
-
* feat: `Bone.migrate()` to run migrations
|
|
407
|
-
* feat: `Bone.bulkCreate()` to bulk insert records
|
|
408
|
-
* feat: `require('leoric')` now exports `Realm` to connect with multiple databases
|
|
409
|
-
* feat: `realm.define()` to define models in an old fashioned way
|
|
410
|
-
* feat: `realm.connect()` to connect with database
|
|
411
|
-
* feat: SQLite support without hacking node-sqlite3
|
|
412
|
-
* feat: `Bone.DataTypes` for type references
|
|
413
|
-
* feat: `Bone.init()` to initialize models
|
|
414
|
-
* feat: an adaptor to use Leoric in (partially) Sequelize complaint API
|
|
415
|
-
* refactor: a complete re-write of JOIN queries
|
|
416
|
-
* refactor: added `Bone.driver` to better encapsulate and planish database nuances
|
|
417
|
-
|
|
418
|
-
0.4.5 / 2019-12-14
|
|
419
|
-
==================
|
|
420
|
-
|
|
421
|
-
* fix: prevent primary key from being overridden with incorrect `LAST_INSERT_ID()`
|
|
422
|
-
|
|
423
|
-
0.4.4 / 2019-07-15
|
|
424
|
-
==================
|
|
425
|
-
|
|
426
|
-
* fix: append default scope when declaring relations, fixes #10
|
|
427
|
-
|
|
428
|
-
0.4.3 / 2019-05-09
|
|
429
|
-
==================
|
|
430
|
-
|
|
431
|
-
* fix: prevent Bone.dispatch from creating duplicated records of main table
|
|
432
|
-
|
|
433
|
-
0.4.2 / 2019-04-26
|
|
434
|
-
==================
|
|
435
|
-
|
|
436
|
-
* feat: `Spell#orWhere()` and `Spell#orHaving()`
|
|
437
|
-
* feat: arithmetic operators
|
|
438
|
-
* feat: unary operators such as unary minus `-` and bit invertion `~`
|
|
439
|
-
* fix: unset attribute should be overwritable
|
|
440
|
-
* fix: `attributeChanged()` should be false if attribute is unset and not overwritten
|
|
441
|
-
* fix: subclass with incomplete getter/setter should be complemented
|
|
442
|
-
* fix: sharding key validation on `Bone.update()` and `Bone.save()`
|
|
443
|
-
* fix: sharding key should be along with primary key on `bone.remove()`
|
|
444
|
-
* fix: `Bone.cast()` should leave `null` as is
|
|
445
|
-
* fix: `INSERT ... UPDATE` with `id = LAST_INSERT_ID(id)` in MySQL
|
|
446
|
-
* fix: `Model.find({ name: { $op1, $op2 } })` object conditions with multiple operators
|
|
447
|
-
* fix: prefixing result set with qualifiers if query contains join relations and is not dispatchable
|
|
448
|
-
* fix: `Spell#$get(index)` with LIMIT
|
|
449
|
-
* doc: `Model.transaction()`
|
|
450
|
-
* doc: definition types with `index.d.ts`
|
|
451
|
-
|
|
452
|
-
0.4.1 / 2019-03-21
|
|
453
|
-
==================
|
|
454
|
-
|
|
455
|
-
* feat: premature sharding key validation
|
|
456
|
-
* fix: output complete SQL instead of parameterized query with values.
|
|
457
|
-
* fix: both `connect({ model })` and `connect({ models })` are allowed.
|
|
458
|
-
* doc: no more `.findOrCreate()`, just `.upsert()`
|
|
459
|
-
* doc: table of contents with kramdown's `{:toc}`
|
|
460
|
-
* chore: droped experimental sqlite3 support
|
|
461
|
-
|
|
462
|
-
0.4.0 / 2018-11-05
|
|
463
|
-
==================
|
|
464
|
-
|
|
465
|
-
* feat: PostgreSQL support
|
|
466
|
-
* feat: Transaction support
|
|
467
|
-
* upgrade: (forked) SQLite client updated to SQLite 3.24
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
0.3.0 / 2018-10-31
|
|
471
|
-
==================
|
|
472
|
-
|
|
473
|
-
* feat: SQLite support with a [forked sqlite3](https://github.com/cyjake/node-sqlite3)
|
|
474
|
-
* feat: mysql2 support (which is trivial since both mysql and mysql2 share the same API)
|
|
475
|
-
* refactor: Spell now formats SQL with the literals separated, which gets escaped by the corresponding client itself later on.
|
|
476
|
-
|
|
477
|
-
0.2.0 / 2018-01-03
|
|
478
|
-
==================
|
|
479
|
-
|
|
480
|
-
* breaking: renaming
|
|
481
|
-
|
|
482
|
-
0.1.8 / 2017-12-31
|
|
483
|
-
==================
|
|
484
|
-
|
|
485
|
-
* fix: implement `query.batch()` as async iterator
|
|
486
|
-
* fix: `NOT (expr)`
|
|
487
|
-
* fix: `IFNULL(foo, default)`
|
|
488
|
-
* fix: support `.select(name[])`, `.select(name => {})`, and `.select("...name")`
|
|
489
|
-
* doc: `Model => className` in association options
|
|
490
|
-
* doc: use [jsdoc](http://usejsdoc.org) to generate docs/api
|
|
491
|
-
* doc: `.include()`
|
|
492
|
-
|
|
493
|
-
0.1.7 / 2017-12-22
|
|
494
|
-
==================
|
|
495
|
-
|
|
496
|
-
* refactor: `{ type: 'op', name: 'as' }` renamed to `{ type: 'alias' }`
|
|
497
|
-
* feat: `{ type: 'mod' }` for modifier, currently only `DISTINCT` is recognized
|
|
498
|
-
* feat: unary operators like `!` and `NOT`
|
|
499
|
-
* feat: `IS` and `IS NOT`
|
|
500
|
-
* fix: logic operator precendences
|
|
501
|
-
* fix: polymorphic hasMany({ through }) relations
|
|
502
|
-
* fix: dispatching multiple results with joins correctly
|
|
503
|
-
|
|
504
|
-
0.1.6 / 2017-12-21
|
|
505
|
-
==================
|
|
506
|
-
|
|
507
|
-
* feat: proper `.first`, `.last`, `.all`, and `.get(index)`
|
|
508
|
-
* fix: accept `Date`, `boolean`, and `Set` values
|
|
509
|
-
* fix: `Model.unscoped`
|
|
510
|
-
* fix: `Model.remove({}, true)` should be unscoped
|
|
511
|
-
|
|
512
|
-
0.1.5 / 2017-12-20
|
|
513
|
-
==================
|
|
514
|
-
|
|
515
|
-
* refactor: encapsulate column names. Keep them from the users even if the query results can not be dispatched.
|
|
516
|
-
* fix: complicated groups with joins should discard the use of subquery.
|
|
517
|
-
* fix: camelCase should replace globally
|
|
518
|
-
* fix: avoid missing attribtue exception when toJSON/toObject
|
|
519
|
-
|
|
520
|
-
0.1.4 / 2017-12-18
|
|
521
|
-
==================
|
|
522
|
-
|
|
523
|
-
* fix: should format condition arrays by hand instead of hand it over to formatExpr
|
|
524
|
-
* fix: whereConditions of subquery should retain the order of the whereConditions in major query
|
|
525
|
-
* fix: calculated columns should be kept in the final columns when sorting out the attributes
|
|
526
|
-
* fix: doesn't depend on co anymore
|
|
527
|
-
|
|
528
|
-
0.1.3 / 2017-12-17
|
|
529
|
-
==================
|
|
530
|
-
|
|
531
|
-
* fix: `select distict foo from table`;
|
|
532
|
-
* fix: `where (a = 1 or a = 2) and b = 3`;
|
|
533
|
-
* doc: a syntax table to provide a better glance over the querying ability.
|
|
534
|
-
|
|
535
|
-
0.1.2 / 2017-12-14
|
|
536
|
-
==================
|
|
537
|
-
|
|
538
|
-
* fix: copy left table's orders into subquery to make order/limit work when combined.
|
|
539
|
-
* fix: errors should be thrown when accessing attributes that weren't selected at the first place.
|
|
540
|
-
|
|
541
|
-
0.1.1 / 2017-12-13
|
|
542
|
-
==================
|
|
543
|
-
|
|
544
|
-
* refactor: automatic versioning on spells. When client calls query methods with chaining, new versions of spell gets duplicated. Makes reuse of spells possible.
|
|
545
|
-
* doc: english verion is almost complete <http://cyj.me/leoric>.
|
|
546
|
-
|
|
547
|
-
0.1.0 / 2017-12-09
|
|
548
|
-
==================
|
|
549
|
-
|
|
550
|
-
* Initial version, covers basic usage such as model authoring, database connection, query interface, and association.
|