leoric 1.15.1 → 2.0.3

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/History.md CHANGED
@@ -1,3 +1,54 @@
1
+ 2.0.3 / 2021-02-11
2
+ ==================
3
+
4
+ ## What's Changed
5
+ * fix: default updatedAt to new date if model has no createdAt by @LB4027221 in https://github.com/cyjake/leoric/pull/268
6
+
7
+ ## New Contributors
8
+ * @LB4027221 made their first contribution in https://github.com/cyjake/leoric/pull/268
9
+
10
+ **Full Changelog**: https://github.com/cyjake/leoric/compare/v2.0.2...v2.0.3
11
+
12
+ 2.0.2 / 2021-02-10
13
+ ==================
14
+
15
+ ## What's Changed
16
+ * fix: order by alias should not throw by @cyjake in https://github.com/cyjake/leoric/pull/255
17
+ * fix: fix #257 DataType.uncast should skip Raw type at type checking by @JimmyDaddy in https://github.com/cyjake/leoric/pull/258
18
+ * docs: async function in transaction by @cyjake in https://github.com/cyjake/leoric/pull/259
19
+ * fix: fixed #256 static create instance should check all default attri… by @JimmyDaddy in https://github.com/cyjake/leoric/pull/262
20
+ * fix: fix #260 UPDATE with LIMIT and ORDER should be formatted(mysql only) by @JimmyDaddy in https://github.com/cyjake/leoric/pull/261
21
+ * refactor: keep the UPDATE ... ORDER BY ... LIMIT to mysql driver by @cyjake in https://github.com/cyjake/leoric/pull/264
22
+ * fix: fix #263 upsert attributes should use defaultValue while there i… by @JimmyDaddy in https://github.com/cyjake/leoric/pull/265
23
+ * fix: fix restore Error `Undefined attribute "deletedAt"` by @JimmyDaddy in https://github.com/cyjake/leoric/pull/267
24
+ * fix: type checking adaption by @JimmyDaddy in https://github.com/cyjake/leoric/pull/266
25
+
26
+
27
+ **Full Changelog**: https://github.com/cyjake/leoric/compare/v2.0.1...v2.0.2
28
+
29
+ 2.0.1 / 2022-01-05
30
+ ==================
31
+
32
+ ## What's Changed
33
+ * fix: format numeric result by @JimmyDaddy in https://github.com/cyjake/leoric/pull/253
34
+ * fix: should still return number if value is '0.000' by @cyjake in https://github.com/cyjake/leoric/pull/254
35
+
36
+ **Full Changelog**: https://github.com/cyjake/leoric/compare/v1.15.1...v2.0.1
37
+
38
+ 2.0.0 / 2021-12-28
39
+ ==================
40
+
41
+ ## What's Changed
42
+ * breaking: model.sync add force/alter option by @SmartOrange in https://github.com/cyjake/leoric/pull/224
43
+ * breaking: logQueryError(err, sql, duration, options) by @cyjake in https://github.com/cyjake/leoric/pull/237
44
+ * test: add utf8mb4 test cases by @fengmk2 in https://github.com/cyjake/leoric/pull/239
45
+ * Merge 1.x changes by @cyjake in https://github.com/cyjake/leoric/pull/249
46
+
47
+ ## New Contributors
48
+ * @SmartOrange made their first contribution in https://github.com/cyjake/leoric/pull/222
49
+
50
+ **Full Changelog**: https://github.com/cyjake/leoric/compare/v1.15.1...v2.0.0
51
+
1
52
  1.15.1 / 2021-12-28
2
53
  ===================
3
54
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leoric",
3
- "version": "1.15.1",
3
+ "version": "2.0.3",
4
4
  "description": "JavaScript Object-relational mapping alchemy",
5
5
  "main": "index.js",
6
6
  "types": "types/index.d.ts",
@@ -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,6 +253,7 @@ module.exports = Bone => {
256
253
  } else {
257
254
  instance = new this(values, options);
258
255
  }
256
+
259
257
  return instance;
260
258
  }
261
259
 
@@ -469,6 +467,7 @@ module.exports = Bone => {
469
467
  const { where, paranoid, individualHooks } = options;
470
468
  if (individualHooks) {
471
469
  let findSpell = this._find(where, options);
470
+ translateOptions(findSpell, options);
472
471
  if (paranoid === false) findSpell = findSpell.unparanoid;
473
472
  const instances = await findSpell;
474
473
  if (instances.length) {
@@ -484,6 +483,7 @@ module.exports = Bone => {
484
483
  const { where, paranoid = false, validate } = options;
485
484
  const whereConditions = where || {};
486
485
  const spell = super.update(whereConditions, values, { validate, hooks: false, ...options });
486
+ translateOptions(spell, options);
487
487
  if (!paranoid) return spell.unparanoid;
488
488
  return spell;
489
489
  }
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
  *
@@ -280,21 +298,7 @@ class Bone {
280
298
 
281
299
  // merge all changed values
282
300
  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
- }
301
+ valuesValidate(changedValues, attributes, this);
298
302
  }
299
303
 
300
304
  /**
@@ -306,22 +310,7 @@ class Bone {
306
310
  */
307
311
  static _validateAttributes(values = {}) {
308
312
  const { attributes } = this;
309
- for (const valueKey in values) {
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
- }
313
+ valuesValidate(values, attributes, this);
325
314
  }
326
315
 
327
316
  /**
@@ -722,9 +711,10 @@ class Bone {
722
711
  }
723
712
 
724
713
  if (attributes[updatedAt] && !this[updatedAt]) {
725
- this[updatedAt] = this[createdAt];
714
+ this[updatedAt] = this[createdAt] || new Date();
726
715
  }
727
716
 
717
+ const validateValues = {};
728
718
  for (const name in attributes) {
729
719
  const value = this.attribute(name);
730
720
  const { defaultValue } = attributes[name];
@@ -733,10 +723,12 @@ class Bone {
733
723
  } else if (value === undefined && defaultValue != null) {
734
724
  data[name] = defaultValue;
735
725
  }
726
+ if (attributes[name].primaryKey) continue;
727
+ validateValues[name] = data[name];
736
728
  }
737
729
 
738
730
  if (opts.validate !== false) {
739
- this._validateAttributes();
731
+ this._validateAttributes(validateValues);
740
732
  }
741
733
 
742
734
  const spell = new Spell(Model, opts).$insert(data);
@@ -822,10 +814,10 @@ class Bone {
822
814
 
823
815
  const conditions = {
824
816
  [primaryKey]: this[primaryKey],
825
- deletedAt: { $ne: null },
817
+ [deletedAt]: { $ne: null },
826
818
  };
827
819
  if (shardingKey) conditions[shardingKey] = this[shardingKey];
828
- await this.update({ deletedAt: null }, { ...opts, paranoid: false });
820
+ await this.update({ [deletedAt]: null }, { ...opts, paranoid: false });
829
821
  return this;
830
822
  }
831
823
 
@@ -840,7 +832,7 @@ class Bone {
840
832
  if (deletedAt == null) {
841
833
  throw new Error('Model is not paranoid');
842
834
  }
843
- return Bone.update.call(this, conditions, { deletedAt: null }, { ...opts, paranoid: false });
835
+ return Bone.update.call(this, conditions, { [deletedAt]: null }, { ...opts, paranoid: false });
844
836
  }
845
837
 
846
838
  /**
@@ -855,9 +847,13 @@ class Bone {
855
847
  const data = {};
856
848
  const Model = this;
857
849
  const { attributes } = Model;
858
- for (const name in values) {
859
- if (this.hasAttribute(name)) {
860
- data[name] = values[name];
850
+ for (const key in attributes) {
851
+ const attribute = attributes[key];
852
+ if (attribute.primaryKey) continue;
853
+ if (values[key] == null && attribute.defaultValue != null) {
854
+ data[key] = attribute.defaultValue;
855
+ } else if (values[key] !== undefined){
856
+ data[key] = values[key];
861
857
  }
862
858
  }
863
859
 
@@ -940,6 +936,15 @@ class Bone {
940
936
  const attribute = new Attribute(name, attributes[name], options.define);
941
937
  attributeMap[attribute.columnName] = attribute;
942
938
  attributes[name] = attribute;
939
+ if (TIMESTAMP_NAMES.includes(name)) {
940
+ const { columnName } = attribute;
941
+ const legacyColumnName = LEGACY_TIMESTAMP_COLUMN_MAP[columnName];
942
+ if (!columnMap[columnName] && legacyColumnName && columnMap[legacyColumnName]) {
943
+ // correct columname
944
+ attribute.columnName = legacyColumnName;
945
+ attributeMap[attribute.columnName] = attribute;
946
+ }
947
+ }
943
948
  const columnInfo = columnMap[attribute.columnName];
944
949
  // if datetime or timestamp precision not defined, default to column info
945
950
  if (columnInfo && attribute.type instanceof DataTypes.DATE && attribute.type.precision == null) {
@@ -949,7 +954,7 @@ class Bone {
949
954
 
950
955
  const primaryKey = Object.keys(attributes).find(key => attributes[key].primaryKey);
951
956
  const timestamps = {};
952
- for (const key of [ 'createdAt', 'updatedAt', 'deletedAt' ]) {
957
+ for (const key of TIMESTAMP_NAMES) {
953
958
  const name = attributes.hasOwnProperty(key) ? key : snakeCase(key);
954
959
  const attribute = attributes[name];
955
960
 
@@ -1318,13 +1323,9 @@ class Bone {
1318
1323
  static create(values, opts = {}) {
1319
1324
  const data = Object.assign({}, values);
1320
1325
  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
1326
  // static create proxy to instance.create
1325
1327
  return instance.create({
1326
1328
  ...opts,
1327
- validate: false, // should not validate again
1328
1329
  });
1329
1330
  }
1330
1331
 
@@ -1544,7 +1545,7 @@ class Bone {
1544
1545
  Object.defineProperties(this, looseReadonly({ ...hookMethods, attributes, table }));
1545
1546
  }
1546
1547
 
1547
- static async sync() {
1548
+ static async sync({ force = false, alter = false } = {}) {
1548
1549
  const { driver, physicTable: table } = this;
1549
1550
  const { database } = this.options;
1550
1551
 
@@ -1569,7 +1570,14 @@ class Bone {
1569
1570
  if (columns.length === 0) {
1570
1571
  await driver.createTable(table, attributes);
1571
1572
  } else {
1572
- await driver.alterTable(table, compare(attributes, columnMap));
1573
+ if (force) {
1574
+ await driver.dropTable(table);
1575
+ await driver.createTable(table, attributes);
1576
+ } else if (alter){
1577
+ await driver.alterTable(table, compare(attributes, columnMap));
1578
+ } else {
1579
+ console.warn('[synchronize_fail] %s couldn\'t be synchronized, please use force or alter to specify execution', this.name);
1580
+ }
1573
1581
  }
1574
1582
 
1575
1583
  const schemaInfo = await driver.querySchemaInfo(database, table);
package/src/collection.js CHANGED
@@ -1,5 +1,8 @@
1
1
  'use strict';
2
2
 
3
+ const { AGGREGATOR_MAP } = require('./constants');
4
+
5
+ const AGGREGATORS = Object.values(AGGREGATOR_MAP);
3
6
  /**
4
7
  * An extended Array to represent collections of models.
5
8
  */
@@ -87,8 +90,14 @@ function dispatch(spell, rows, fields) {
87
90
  const { type, value, args } = columns[0];
88
91
  if (type === 'alias' && args && args[0].type === 'func') {
89
92
  const row = rows[0];
90
- const result = row && (row[''] || row[table]);
91
- return result && result[value] || 0;
93
+ const record = row && (row[''] || row[table]);
94
+ const result = record && record[value];
95
+ // see https://www.w3schools.com/mysql/mysql_ref_functions.asp
96
+ if (AGGREGATORS.includes(args[0].name)) {
97
+ const num = Number(result);
98
+ return isNaN(num) ? result : num;
99
+ }
100
+ return result;
92
101
  }
93
102
  }
94
103
 
@@ -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)) throw new Error(util.format('invalid integer: %s', originValue));
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
  };
@@ -93,7 +93,7 @@ class MysqlDriver extends AbstractDriver {
93
93
  try {
94
94
  result = await promise;
95
95
  } catch (err) {
96
- logger.logQueryError(sql, err, calculateDuration(start), opts);
96
+ logger.logQueryError(err, sql, calculateDuration(start), opts);
97
97
  throw err;
98
98
  } finally {
99
99
  if (!opts.connection) connection.release();
@@ -58,4 +58,21 @@ 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
+ },
61
78
  };
@@ -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;
@@ -137,7 +137,7 @@ class PostgresDriver extends AbstractDriver {
137
137
  try {
138
138
  result = await connection.query(...args);
139
139
  } catch (err) {
140
- logger.logQueryError(formatted, err, calculateDuration(start), spell);
140
+ logger.logQueryError(err, formatted, calculateDuration(start), spell);
141
141
  throw err;
142
142
  } finally {
143
143
  if (!spell.connection) connection.release();
@@ -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;
@@ -48,7 +48,7 @@ class SqliteDriver extends AbstractDriver {
48
48
  try {
49
49
  result = await connection.query(query, values, opts);
50
50
  } catch (err) {
51
- logger.logQueryError(sql, err, calculateDuration(start), opts);
51
+ logger.logQueryError(err, sql, calculateDuration(start), opts);
52
52
  throw err;
53
53
  } finally {
54
54
  if (!opts.connection) connection.release();
@@ -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
@@ -164,12 +159,12 @@ class Realm {
164
159
  return this.Bone;
165
160
  }
166
161
 
167
- async sync() {
162
+ async sync(options) {
168
163
  if (!this.connected) await this.connect();
169
164
  const { models } = this;
170
165
 
171
166
  for (const model of Object.values(models)) {
172
- await model.sync();
167
+ await model.sync(options);
173
168
  }
174
169
  }
175
170
 
package/src/spell.js CHANGED
@@ -11,6 +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('./constants');
14
15
 
15
16
  /**
16
17
  * Parse condition expressions
@@ -903,14 +904,6 @@ class Spell {
903
904
  }
904
905
  }
905
906
 
906
- const AGGREGATOR_MAP = {
907
- count: 'count',
908
- average: 'avg',
909
- minimum: 'min',
910
- maximum: 'max',
911
- sum: 'sum'
912
- };
913
-
914
907
  for (const aggregator in AGGREGATOR_MAP) {
915
908
  const func = AGGREGATOR_MAP[aggregator];
916
909
  Object.defineProperty(Spell.prototype, `$${aggregator}`, {
package/types/index.d.ts CHANGED
@@ -64,10 +64,10 @@ export class Spell<T extends typeof Bone, U = InstanceType<T> | Collection<Insta
64
64
  constructor(Model: T, opts: SpellOptions);
65
65
 
66
66
  select(...names: Array<string | RawSql> | Array<(name: string) => boolean>): Spell<T, U>;
67
- insert(opts: SetOptions): Spell<T, number>;
68
- update(opts: SetOptions): Spell<T, number>;
69
- upsert(opts: SetOptions): Spell<T, number>;
70
- delete(): Spell<T, number>;
67
+ insert(opts: SetOptions): Spell<T, QueryResult>;
68
+ update(opts: SetOptions): Spell<T, QueryResult>;
69
+ upsert(opts: SetOptions): Spell<T, QueryResult>;
70
+ delete(): Spell<T, QueryResult>;
71
71
 
72
72
  from(table: string | Spell<T>): Spell<T, U>;
73
73
 
@@ -107,8 +107,8 @@ export class Spell<T extends typeof Bone, U = InstanceType<T> | Collection<Insta
107
107
 
108
108
  batch(size?: number): AsyncIterable<T>;
109
109
 
110
- increment(name: string, by?: number, options?: QueryOptions): Spell<T, number>;
111
- decrement(name: string, by?: number, options?: QueryOptions): Spell<T, number>;
110
+ increment(name: string, by?: number, options?: QueryOptions): Spell<T, QueryResult>;
111
+ decrement(name: string, by?: number, options?: QueryOptions): Spell<T, QueryResult>;
112
112
 
113
113
  toSqlString(): string;
114
114
  toString(): string;