leoric 1.13.5 → 1.14.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 +64 -0
- package/Readme.md +1 -1
- package/package.json +1 -1
- package/src/adapters/sequelize.js +25 -7
- package/src/bone.js +75 -56
- package/src/drivers/abstract/attribute.js +5 -5
- package/src/drivers/abstract/logger.js +8 -0
- package/src/drivers/mysql/index.js +5 -2
- package/src/drivers/postgres/attribute.js +26 -0
- package/src/drivers/postgres/index.js +1 -1
- package/src/drivers/postgres/schema.js +1 -1
- package/src/drivers/sqlite/index.js +1 -1
- package/src/expr_formatter.js +1 -1
- package/src/realm.js +20 -1
- package/types/data_types.d.ts +88 -0
- package/types/index.d.ts +25 -21
package/History.md
CHANGED
|
@@ -1,3 +1,67 @@
|
|
|
1
|
+
1.14.3 / 2021-11-12
|
|
2
|
+
===================
|
|
3
|
+
|
|
4
|
+
## What's Changed
|
|
5
|
+
* fix: logger.logQuery should be guarded in case of error by @SmartOrange in https://github.com/cyjake/leoric/pull/222
|
|
6
|
+
* fix: findOne without result should return null by @JimmyDaddy in https://github.com/cyjake/leoric/pull/225
|
|
7
|
+
* fix: Literal should support bigint type by @fengmk2 in https://github.com/cyjake/leoric/pull/226
|
|
8
|
+
* fix: select((name: string) => boolean) by @cyjake in https://github.com/cyjake/leoric/pull/227
|
|
9
|
+
|
|
10
|
+
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.14.2...v1.14.3
|
|
11
|
+
|
|
12
|
+
1.14.2 / 2021-11-01
|
|
13
|
+
===================
|
|
14
|
+
|
|
15
|
+
## What's Changed
|
|
16
|
+
* fix: accept timestamps in snake case by @cyjake in https://github.com/cyjake/leoric/pull/221
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.14.1...v1.14.2
|
|
20
|
+
|
|
21
|
+
1.14.1 / 2021-11-01
|
|
22
|
+
===================
|
|
23
|
+
|
|
24
|
+
## What's Changed
|
|
25
|
+
* docs: export { Collection } by @cyjake in https://github.com/cyjake/leoric/pull/220
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.14.0...v1.14.1
|
|
29
|
+
|
|
30
|
+
1.14.0 / 2021-11-01
|
|
31
|
+
===================
|
|
32
|
+
|
|
33
|
+
Two options regarding `Model.init()` were added in this release:
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
class User extends Bone {}
|
|
37
|
+
User.init({ name: STRING }, {
|
|
38
|
+
timestamps: true, // which is the default
|
|
39
|
+
paranoid: true, // which default to `false`
|
|
40
|
+
});
|
|
41
|
+
assert.deepEqual(Object.keys(User.attributes), [
|
|
42
|
+
'id',
|
|
43
|
+
'name',
|
|
44
|
+
'createdAt',
|
|
45
|
+
'updatedAt',
|
|
46
|
+
'deletedAt',
|
|
47
|
+
]);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## What's Changed
|
|
51
|
+
* docs: update 'primayKey' typos by @freshgum-bubbles in https://github.com/cyjake/leoric/pull/211
|
|
52
|
+
* docs: DataTypes definitions in d.ts by @cyjake in https://github.com/cyjake/leoric/pull/210
|
|
53
|
+
* fix: fix#209 sequelize mode should update all changed fields in instance update method by @JimmyDaddy in https://github.com/cyjake/leoric/pull/212
|
|
54
|
+
* fix: fix #213 findAndCountAll should ignore attributes by @JimmyDaddy in https://github.com/cyjake/leoric/pull/214
|
|
55
|
+
* fix: opts.connectTimeout by @cyjake in https://github.com/cyjake/leoric/pull/216
|
|
56
|
+
* fix: reload instance with sharding key should not throw by @cyjake in https://github.com/cyjake/leoric/pull/217
|
|
57
|
+
* feat: timestamps should be defined by default by @cyjake in https://github.com/cyjake/leoric/pull/218
|
|
58
|
+
* fix: instance.reload() should not rely on `static findOne()` by @cyjake in https://github.com/cyjake/leoric/pull/219
|
|
59
|
+
|
|
60
|
+
## New Contributors
|
|
61
|
+
* @freshgum-bubbles made their first contribution in https://github.com/cyjake/leoric/pull/211
|
|
62
|
+
|
|
63
|
+
**Full Changelog**: https://github.com/cyjake/leoric/compare/v1.13.5...v1.14.0
|
|
64
|
+
|
|
1
65
|
1.13.5 / 2021-10-26
|
|
2
66
|
===================
|
|
3
67
|
|
package/Readme.md
CHANGED
|
@@ -48,7 +48,7 @@ If table structures were intended to be maintained in the models, Leoric can be
|
|
|
48
48
|
const { BIGINT, STRING } = Bone.DataTypes;
|
|
49
49
|
class Post extends Bone {
|
|
50
50
|
static attributes = {
|
|
51
|
-
id: { type: BIGINT,
|
|
51
|
+
id: { type: BIGINT, primaryKey: true },
|
|
52
52
|
email: { type: STRING, allowNull: false },
|
|
53
53
|
nickname: { type: STRING, allowNull: false },
|
|
54
54
|
}
|
package/package.json
CHANGED
|
@@ -331,6 +331,7 @@ module.exports = Bone => {
|
|
|
331
331
|
let spell = this._find({}, filterOptions(options));
|
|
332
332
|
translateOptions(spell, options);
|
|
333
333
|
let spellCount = this._find({}, filterOptions(options));
|
|
334
|
+
delete options.attributes;
|
|
334
335
|
translateOptions(spellCount, options);
|
|
335
336
|
delete spellCount.rowCount;
|
|
336
337
|
delete spellCount.skip;
|
|
@@ -378,7 +379,7 @@ module.exports = Bone => {
|
|
|
378
379
|
// findAll maybe override by developer, that will make it return a non-Spell object
|
|
379
380
|
spell = this._find({}, filterOptions(options));
|
|
380
381
|
translateOptions(spell, { ...options, limit: 1 });
|
|
381
|
-
spell = spell.later(result => result[0]);
|
|
382
|
+
spell = spell.later(result => result[0] != null? result[0]: null);
|
|
382
383
|
}
|
|
383
384
|
if (options && options.paranoid === false) return spell.unparanoid;
|
|
384
385
|
return spell;
|
|
@@ -496,29 +497,46 @@ module.exports = Bone => {
|
|
|
496
497
|
async update(values = {}, options = {}) {
|
|
497
498
|
const { fields } = options;
|
|
498
499
|
const changeValues = {};
|
|
500
|
+
const originalValues = Object.assign({}, this.getRaw());
|
|
501
|
+
for (const name in values) {
|
|
502
|
+
if (values[name] !== undefined && this.hasAttribute(name)) {
|
|
503
|
+
// exec custom setters in case it exist
|
|
504
|
+
this[name] = values[name];
|
|
505
|
+
changeValues[name] = this.attribute(name);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
499
509
|
const changedKeys = this.changed();
|
|
500
510
|
if (changedKeys) {
|
|
501
511
|
for (const name of changedKeys) {
|
|
502
512
|
// custom getter should be executed in case there is a custom setter
|
|
503
|
-
changeValues[name] = this[name];
|
|
513
|
+
if (!(name in changeValues)) changeValues[name] = this[name];
|
|
504
514
|
}
|
|
505
515
|
}
|
|
506
516
|
|
|
507
517
|
let changes = {};
|
|
508
518
|
if (fields && fields.length) {
|
|
509
519
|
fields.map(key => {
|
|
510
|
-
if (
|
|
511
|
-
else
|
|
520
|
+
if (changeValues[key] !== undefined) changes[key] = changeValues[key];
|
|
521
|
+
else if (values[key] !== undefined) changes[key] = values[key];
|
|
522
|
+
else changes[key] = this.attribute(key);
|
|
512
523
|
});
|
|
513
524
|
} else {
|
|
514
525
|
changes = {
|
|
515
|
-
...changeValues,
|
|
516
526
|
...values,
|
|
527
|
+
...changeValues,
|
|
517
528
|
};
|
|
518
529
|
}
|
|
519
|
-
const spell = super._update(changes, options);
|
|
520
530
|
// instance update don't need to be paranoid
|
|
521
|
-
|
|
531
|
+
options.paranoid = false;
|
|
532
|
+
try {
|
|
533
|
+
const res = await super._update(changes, options);
|
|
534
|
+
return res;
|
|
535
|
+
} catch (err) {
|
|
536
|
+
// revert value in case update failed
|
|
537
|
+
this._setRaw(originalValues);
|
|
538
|
+
throw err;
|
|
539
|
+
}
|
|
522
540
|
}
|
|
523
541
|
|
|
524
542
|
// EXISTS
|
package/src/bone.js
CHANGED
|
@@ -15,12 +15,6 @@ const { capitalize, camelCase, snakeCase } = require('./utils/string');
|
|
|
15
15
|
const { hookNames, setupSingleHook } = require('./setup_hooks');
|
|
16
16
|
const { logger } = require('./utils/index');
|
|
17
17
|
|
|
18
|
-
const LEGACY_TIMESTAMP_MAP = {
|
|
19
|
-
gmtCreate: 'createdAt',
|
|
20
|
-
gmtModified: 'updatedAt',
|
|
21
|
-
gmtDeleted: 'deletedAt',
|
|
22
|
-
};
|
|
23
|
-
|
|
24
18
|
function looseReadonly(props) {
|
|
25
19
|
return Object.keys(props).reduce((result, name) => {
|
|
26
20
|
result[name] = {
|
|
@@ -243,8 +237,13 @@ class Bone {
|
|
|
243
237
|
}
|
|
244
238
|
|
|
245
239
|
// protected
|
|
246
|
-
_setRaw(
|
|
247
|
-
|
|
240
|
+
_setRaw(...args) {
|
|
241
|
+
const [ name, value ] = args;
|
|
242
|
+
if (args.length > 1) {
|
|
243
|
+
this.#raw[name] = value;
|
|
244
|
+
} else if (args.length === 1 && name !== undefined && typeof name === 'object') {
|
|
245
|
+
this.#raw = name;
|
|
246
|
+
}
|
|
248
247
|
}
|
|
249
248
|
|
|
250
249
|
// protected
|
|
@@ -626,13 +625,31 @@ class Bone {
|
|
|
626
625
|
/**
|
|
627
626
|
* Persist changes on current instance back to database with `UPDATE`.
|
|
628
627
|
* @public
|
|
629
|
-
* @param {Object}
|
|
628
|
+
* @param {Object} values
|
|
630
629
|
* @param {Object?} options
|
|
631
630
|
* @returns {number} affected rows
|
|
632
631
|
* @memberof Bone
|
|
633
632
|
*/
|
|
634
|
-
update(
|
|
635
|
-
|
|
633
|
+
async update(values, options = {}) {
|
|
634
|
+
const changes = {};
|
|
635
|
+
const originalValues = Object.assign({}, this.#raw);
|
|
636
|
+
if (typeof values === 'object') {
|
|
637
|
+
for (const name in values) {
|
|
638
|
+
if (values[name] !== undefined && this.hasAttribute(name)) {
|
|
639
|
+
// exec custom setters in case it exist
|
|
640
|
+
this[name] = values[name];
|
|
641
|
+
changes[name] = this.attribute(name);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
try {
|
|
646
|
+
const res = await this._update(Object.keys(changes).length? changes : values, options);
|
|
647
|
+
return res;
|
|
648
|
+
} catch (error) {
|
|
649
|
+
// revert value in case update failed
|
|
650
|
+
this._setRaw(originalValues);
|
|
651
|
+
throw error;
|
|
652
|
+
}
|
|
636
653
|
}
|
|
637
654
|
|
|
638
655
|
/**
|
|
@@ -640,23 +657,19 @@ class Bone {
|
|
|
640
657
|
* @private
|
|
641
658
|
* @return {number}
|
|
642
659
|
*/
|
|
643
|
-
_update(values, options = {}) {
|
|
644
|
-
const changes = {};
|
|
660
|
+
async _update(values, options = {}) {
|
|
645
661
|
const Model = this.constructor;
|
|
646
662
|
const { attributes, primaryKey, shardingKey } = Model;
|
|
647
|
-
|
|
663
|
+
let changes = {};
|
|
648
664
|
if (values == null) {
|
|
649
665
|
for (const name in attributes) {
|
|
650
666
|
if (this.changed(name)) changes[name] = this.attribute(name);
|
|
651
667
|
}
|
|
652
|
-
} else
|
|
653
|
-
for (const
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
changes[name] = this.attribute(name);
|
|
658
|
-
// revert value in case update failed
|
|
659
|
-
this.attribute(name, originValue);
|
|
668
|
+
} else {
|
|
669
|
+
for (const key in values) {
|
|
670
|
+
if (values[key] !== undefined && this.hasAttribute(key)) {
|
|
671
|
+
changes[key] = values[key];
|
|
672
|
+
}
|
|
660
673
|
}
|
|
661
674
|
}
|
|
662
675
|
|
|
@@ -676,7 +689,7 @@ class Bone {
|
|
|
676
689
|
this._validateAttributes(changes);
|
|
677
690
|
}
|
|
678
691
|
const spell = new Spell(Model, options).$where(where).$update(changes);
|
|
679
|
-
return spell.later(result => {
|
|
692
|
+
return await spell.later(result => {
|
|
680
693
|
// sync changes (changes has been formatted by custom setters, use this.attribute(name, value) directly)
|
|
681
694
|
for (const key in changes) {
|
|
682
695
|
this.attribute(key, changes[key]);
|
|
@@ -737,11 +750,13 @@ class Bone {
|
|
|
737
750
|
}
|
|
738
751
|
|
|
739
752
|
async reload() {
|
|
740
|
-
const { primaryKey } = this.constructor;
|
|
741
|
-
const
|
|
742
|
-
if (
|
|
743
|
-
|
|
744
|
-
|
|
753
|
+
const { primaryKey, shardingKey } = this.constructor;
|
|
754
|
+
const conditions = { [primaryKey]: this[primaryKey] };
|
|
755
|
+
if (shardingKey) conditions[shardingKey] = this[shardingKey];
|
|
756
|
+
const spell = this.constructor._find(conditions).$get(0);
|
|
757
|
+
spell.scopes = [];
|
|
758
|
+
const instance = await spell;
|
|
759
|
+
if (instance) this._clone(instance);
|
|
745
760
|
return instance;
|
|
746
761
|
}
|
|
747
762
|
|
|
@@ -894,27 +909,6 @@ class Bone {
|
|
|
894
909
|
Object.assign(attribute, { jsType });
|
|
895
910
|
}
|
|
896
911
|
|
|
897
|
-
static normalize(attributes) {
|
|
898
|
-
for (const name in LEGACY_TIMESTAMP_MAP) {
|
|
899
|
-
const newName = LEGACY_TIMESTAMP_MAP[name];
|
|
900
|
-
if (attributes.hasOwnProperty(name) && !attributes.hasOwnProperty(newName)) {
|
|
901
|
-
attributes[newName] = attributes[name];
|
|
902
|
-
delete attributes[name];
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
// if there is no primaryKey added, add it to attributes automatically
|
|
907
|
-
if (Object.values(attributes).every(attribute => !attribute.primaryKey)) {
|
|
908
|
-
attributes[this.primaryKey] = {
|
|
909
|
-
type: new DataTypes.BIGINT(),
|
|
910
|
-
allowNull: false,
|
|
911
|
-
autoIncrement: true,
|
|
912
|
-
primaryKey: true,
|
|
913
|
-
columnName: snakeCase(this.primaryKey),
|
|
914
|
-
};
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
|
|
918
912
|
/**
|
|
919
913
|
* Generate attributes from column definitions.
|
|
920
914
|
* @private
|
|
@@ -927,7 +921,18 @@ class Bone {
|
|
|
927
921
|
const table = this.table || snakeCase(pluralize(this.name));
|
|
928
922
|
const tableAlias = camelCase(pluralize(this.name || table));
|
|
929
923
|
|
|
930
|
-
|
|
924
|
+
// if there is no primaryKey added, add it to attributes automatically
|
|
925
|
+
if (Object.values(attributes).every(attribute => !attribute.primaryKey)) {
|
|
926
|
+
attributes[this.primaryKey] = {
|
|
927
|
+
type: new DataTypes.BIGINT(),
|
|
928
|
+
allowNull: false,
|
|
929
|
+
autoIncrement: true,
|
|
930
|
+
columnName: snakeCase(this.primaryKey),
|
|
931
|
+
...attributes[this.primaryKey],
|
|
932
|
+
primaryKey: true,
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
|
|
931
936
|
for (const name of Object.keys(attributes)) {
|
|
932
937
|
const attribute = new Attribute(name, attributes[name], options.define);
|
|
933
938
|
attributeMap[attribute.columnName] = attribute;
|
|
@@ -936,13 +941,15 @@ class Bone {
|
|
|
936
941
|
|
|
937
942
|
const primaryKey = Object.keys(attributes).find(key => attributes[key].primaryKey);
|
|
938
943
|
const timestamps = {};
|
|
939
|
-
for (const
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
944
|
+
for (const key of [ 'createdAt', 'updatedAt', 'deletedAt' ]) {
|
|
945
|
+
const name = attributes.hasOwnProperty(key) ? key : snakeCase(key);
|
|
946
|
+
const attribute = attributes[name];
|
|
947
|
+
|
|
948
|
+
if (!attribute) continue;
|
|
949
|
+
if (columns.some(column => column.columnName === attribute.columnName)) {
|
|
950
|
+
timestamps[key] = name;
|
|
943
951
|
}
|
|
944
952
|
}
|
|
945
|
-
|
|
946
953
|
for (const name in attributes) this.loadAttribute(name);
|
|
947
954
|
|
|
948
955
|
Object.defineProperties(this, looseReadonly({
|
|
@@ -1498,14 +1505,25 @@ class Bone {
|
|
|
1498
1505
|
}
|
|
1499
1506
|
|
|
1500
1507
|
static init(attributes = {}, opts = {}, overrides = {}) {
|
|
1501
|
-
const { hooks, tableName: table } = {
|
|
1508
|
+
const { hooks, paranoid, tableName: table, timestamps } = {
|
|
1502
1509
|
underscored: true,
|
|
1510
|
+
timestamps: true,
|
|
1503
1511
|
tableName: this.table,
|
|
1504
1512
|
hooks: {},
|
|
1505
1513
|
...(this.options && this.options.define),
|
|
1506
1514
|
...opts,
|
|
1507
1515
|
};
|
|
1508
1516
|
|
|
1517
|
+
if (timestamps) {
|
|
1518
|
+
const names = [ 'createdAt', 'updatedAt' ];
|
|
1519
|
+
if (paranoid) names.push('deletedAt');
|
|
1520
|
+
for (const name of names) {
|
|
1521
|
+
if (!attributes[name] && !attributes[snakeCase(name)]) {
|
|
1522
|
+
attributes[name] = DataTypes.DATE;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1509
1527
|
const customDescriptors = Object.getOwnPropertyDescriptors(overrides);
|
|
1510
1528
|
Object.defineProperties(this.prototype, customDescriptors);
|
|
1511
1529
|
|
|
@@ -1514,6 +1532,7 @@ class Bone {
|
|
|
1514
1532
|
if (typeof method === 'function') result[key] = method;
|
|
1515
1533
|
return result;
|
|
1516
1534
|
}, {});
|
|
1535
|
+
|
|
1517
1536
|
Object.defineProperties(this, looseReadonly({ ...hookMethods, attributes, table }));
|
|
1518
1537
|
}
|
|
1519
1538
|
|
|
@@ -105,17 +105,17 @@ class Attribute {
|
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
equals(
|
|
109
|
-
if (!
|
|
110
|
-
const props = [ '
|
|
108
|
+
equals(columnInfo) {
|
|
109
|
+
if (!columnInfo) return false;
|
|
110
|
+
const props = [ 'allowNull', 'dataType', 'primaryKey' ];
|
|
111
111
|
for (const prop of props) {
|
|
112
112
|
// SQLite has default value as string even if data type is integer
|
|
113
|
-
if (this[prop] !=
|
|
113
|
+
if (this[prop] != columnInfo[prop]) {
|
|
114
114
|
debug('[attribute] [%s] %s not equal (defined: %s, actual: %s)',
|
|
115
115
|
this.columnName,
|
|
116
116
|
prop,
|
|
117
117
|
this[prop],
|
|
118
|
-
|
|
118
|
+
columnInfo[prop]);
|
|
119
119
|
return false;
|
|
120
120
|
}
|
|
121
121
|
}
|
|
@@ -24,6 +24,14 @@ class Logger {
|
|
|
24
24
|
return SqlString.format(query.sql || query, values);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
tryLogQuery() {
|
|
28
|
+
try {
|
|
29
|
+
this.logQuery(...arguments);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error(error);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
27
35
|
logQuery(sql, duration, opts) {
|
|
28
36
|
debug('[query] [%s] %s', duration, sql);
|
|
29
37
|
}
|
|
@@ -18,6 +18,8 @@ class MysqlDriver extends AbstractDriver {
|
|
|
18
18
|
* @param {string} opts.appName - In some RDMS, appName is used as the actual name of the database
|
|
19
19
|
* @param {string} opts.database
|
|
20
20
|
* @param {string} opts.connectionLimit
|
|
21
|
+
* @param {number} opts.connectTimeout - The milliseconds before a timeout occurs during the initial connection to the MySQL server. (Default: `10000`)
|
|
22
|
+
* @param {string} opts.charset
|
|
21
23
|
* @param {boolean} opts.stringifyObjects - stringify object value in dataValues
|
|
22
24
|
*/
|
|
23
25
|
constructor(opts = {}) {
|
|
@@ -37,7 +39,7 @@ class MysqlDriver extends AbstractDriver {
|
|
|
37
39
|
const client = opts.client || 'mysql';
|
|
38
40
|
const {
|
|
39
41
|
host, port, user, password,
|
|
40
|
-
connectionLimit, charset, stringifyObjects =
|
|
42
|
+
connectTimeout, connectionLimit, charset, stringifyObjects = true,
|
|
41
43
|
} = opts;
|
|
42
44
|
|
|
43
45
|
if (client !== 'mysql' && client !== 'mysql2') {
|
|
@@ -46,6 +48,7 @@ class MysqlDriver extends AbstractDriver {
|
|
|
46
48
|
|
|
47
49
|
return require(client).createPool({
|
|
48
50
|
connectionLimit,
|
|
51
|
+
connectTimeout,
|
|
49
52
|
host,
|
|
50
53
|
port,
|
|
51
54
|
user,
|
|
@@ -93,7 +96,7 @@ class MysqlDriver extends AbstractDriver {
|
|
|
93
96
|
if (!opts.connection) connection.release();
|
|
94
97
|
}
|
|
95
98
|
|
|
96
|
-
logger.
|
|
99
|
+
logger.tryLogQuery(sql, Date.now() - start, opts);
|
|
97
100
|
const [ results, fields ] = result;
|
|
98
101
|
if (fields) return { rows: results, fields };
|
|
99
102
|
return results;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const debug = require('debug')('leoric');
|
|
4
|
+
|
|
3
5
|
const Attribute = require('../abstract/attribute');
|
|
4
6
|
const DataTypes = require('./data_types');
|
|
5
7
|
const { escape, escapeId } = require('./sqlstring');
|
|
@@ -32,6 +34,30 @@ class PostgresAttribute extends Attribute {
|
|
|
32
34
|
}
|
|
33
35
|
return chunks.join(' ');
|
|
34
36
|
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
equals(columnInfo) {
|
|
40
|
+
if (!columnInfo) return false;
|
|
41
|
+
if (this.type.toSqlString() !== columnInfo.columnType.toUpperCase()) {
|
|
42
|
+
debug('[attribute] [%s] columnType not equal (defined: %s, actual: %s)',
|
|
43
|
+
this.columnName,
|
|
44
|
+
this.type.toSqlString(),
|
|
45
|
+
columnInfo.columnType.toUpperCase());
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const props = [ 'allowNull', 'defaultValue', 'primaryKey' ];
|
|
49
|
+
for (const prop of props) {
|
|
50
|
+
if (this[prop] != columnInfo[prop]) {
|
|
51
|
+
debug('[attribute] [%s] %s not equal (defined: %s, actual: %s)',
|
|
52
|
+
this.columnName,
|
|
53
|
+
prop,
|
|
54
|
+
this[prop],
|
|
55
|
+
columnInfo[prop]);
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
35
61
|
}
|
|
36
62
|
|
|
37
63
|
module.exports = PostgresAttribute;
|
|
@@ -141,7 +141,7 @@ class PostgresDriver extends AbstractDriver {
|
|
|
141
141
|
if (!spell.connection) connection.release();
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
logger.
|
|
144
|
+
logger.tryLogQuery(formatted, Date.now() - start, spell);
|
|
145
145
|
return result;
|
|
146
146
|
}
|
|
147
147
|
|
|
@@ -53,7 +53,7 @@ module.exports = {
|
|
|
53
53
|
for (const row of rows) {
|
|
54
54
|
const tableName = row.table_name;
|
|
55
55
|
const columns = schemaInfo[tableName] || (schemaInfo[tableName] = []);
|
|
56
|
-
let { data_type: dataType,
|
|
56
|
+
let { data_type: dataType, character_maximum_length: length } = row;
|
|
57
57
|
|
|
58
58
|
if (dataType === 'character varying') dataType = 'varchar';
|
|
59
59
|
if (dataType === 'timestamp without time zone') dataType = 'timestamp';
|
package/src/expr_formatter.js
CHANGED
|
@@ -11,7 +11,7 @@ const { precedes, walkExpr } = require('./expr');
|
|
|
11
11
|
* @param {Spell} spell
|
|
12
12
|
* @param {string[]} qualifiers
|
|
13
13
|
*/
|
|
14
|
-
|
|
14
|
+
function findModel(spell, qualifiers) {
|
|
15
15
|
const qualifier = qualifiers && qualifiers[0];
|
|
16
16
|
const Model = qualifier && qualifier != spell.Model.tableAlias
|
|
17
17
|
? (spell.joins.hasOwnProperty(qualifier) ? spell.joins[qualifier].Model : null)
|
package/src/realm.js
CHANGED
|
@@ -38,6 +38,17 @@ async function findModels(dir) {
|
|
|
38
38
|
return models;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
const LEGACY_TIMESTAMP_MAP = {
|
|
42
|
+
gmtCreate: 'createdAt',
|
|
43
|
+
gmtModified: 'updatedAt',
|
|
44
|
+
gmtDeleted: 'deletedAt',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* construct model attributes entirely from column definitions
|
|
49
|
+
* @param {Bone} model
|
|
50
|
+
* @param {Array<string, Object>} columns column definitions
|
|
51
|
+
*/
|
|
41
52
|
function initAttributes(model, columns) {
|
|
42
53
|
const attributes = {};
|
|
43
54
|
|
|
@@ -52,7 +63,15 @@ function initAttributes(model, columns) {
|
|
|
52
63
|
};
|
|
53
64
|
}
|
|
54
65
|
|
|
55
|
-
|
|
66
|
+
for (const name in LEGACY_TIMESTAMP_MAP) {
|
|
67
|
+
const newName = LEGACY_TIMESTAMP_MAP[name];
|
|
68
|
+
if (attributes.hasOwnProperty(name) && !attributes.hasOwnProperty(newName)) {
|
|
69
|
+
attributes[newName] = attributes[name];
|
|
70
|
+
delete attributes[name];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
model.init(attributes, { timestamps: false });
|
|
56
75
|
}
|
|
57
76
|
|
|
58
77
|
async function loadModels(Spine, models, opts) {
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
type LENGTH_VARIANTS = 'tiny' | '' | 'medium' | 'long';
|
|
2
|
+
|
|
3
|
+
interface INVOKABLE<T> {
|
|
4
|
+
(length: LENGTH_VARIANTS): T;
|
|
5
|
+
(length: number): T;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default class DataType {
|
|
9
|
+
toSqlString(): string;
|
|
10
|
+
|
|
11
|
+
static STRING: typeof STRING & INVOKABLE<STRING>;
|
|
12
|
+
static INTEGER: typeof INTEGER & INVOKABLE<INTEGER>;
|
|
13
|
+
static BIGINT: typeof BIGINT & INVOKABLE<BIGINT>;
|
|
14
|
+
static TEXT: typeof TEXT & INVOKABLE<TEXT>;
|
|
15
|
+
static BLOB: typeof BLOB & INVOKABLE<BLOB>;
|
|
16
|
+
static JSON: typeof JSON & INVOKABLE<JSON>;
|
|
17
|
+
static JSONB: typeof JSONB & INVOKABLE<JSONB>;
|
|
18
|
+
static BINARY: typeof BINARY & INVOKABLE<BINARY>;
|
|
19
|
+
static VARBINARY: typeof VARBINARY & INVOKABLE<VARBINARY>;
|
|
20
|
+
static DATE: typeof DATE & INVOKABLE<DATE>;
|
|
21
|
+
static DATEONLY: typeof DATEONLY & INVOKABLE<DATEONLY>;
|
|
22
|
+
static BOOLEAN: typeof BOOLEAN & INVOKABLE<BOOLEAN>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare class STRING extends DataType {
|
|
26
|
+
dataType: 'varchar';
|
|
27
|
+
length: number;
|
|
28
|
+
constructor(length: number);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
declare class INTEGER extends DataType {
|
|
32
|
+
dataType: 'integer' | 'bigint';
|
|
33
|
+
length: number;
|
|
34
|
+
constructor(length: number);
|
|
35
|
+
get UNSIGNED(): this;
|
|
36
|
+
get ZEROFILL(): this;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
declare class BIGINT extends INTEGER {
|
|
40
|
+
dataType: 'bigint';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
declare class TEXT extends DataType {
|
|
44
|
+
dataType: 'text';
|
|
45
|
+
length: LENGTH_VARIANTS;
|
|
46
|
+
constructor(length: LENGTH_VARIANTS);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
declare class BLOB extends DataType {
|
|
50
|
+
dataType: 'blob';
|
|
51
|
+
length: LENGTH_VARIANTS;
|
|
52
|
+
constructor(length: LENGTH_VARIANTS)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
declare class JSON extends DataType {
|
|
56
|
+
dataType: 'text' | 'json';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
declare class JSONB extends JSON {
|
|
60
|
+
dataType: 'json';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
declare class BINARY extends DataType {
|
|
64
|
+
dataType: 'binary';
|
|
65
|
+
length: number;
|
|
66
|
+
constructor(length: number);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
declare class VARBINARY extends DataType {
|
|
70
|
+
dataType: 'varbinary';
|
|
71
|
+
length: number;
|
|
72
|
+
constructor(length: number);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
declare class DATE extends DataType {
|
|
76
|
+
dataType: 'date';
|
|
77
|
+
precision: number;
|
|
78
|
+
timezone: boolean;
|
|
79
|
+
constructor(precision: number, timezone?: boolean)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
declare class DATEONLY extends DataType {
|
|
83
|
+
dataType: 'dateonly';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
declare class BOOLEAN extends DataType {
|
|
87
|
+
dataType: 'boolean'
|
|
88
|
+
}
|
package/types/index.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import DataType from './data_types';
|
|
2
|
+
|
|
3
|
+
export { DataType as DataTypes };
|
|
4
|
+
|
|
5
|
+
type DataTypes<T> = {
|
|
6
|
+
[Property in keyof T as Exclude<Property, "toSqlString">]: T[Property]
|
|
7
|
+
}
|
|
8
|
+
|
|
1
9
|
interface ExprIdentifier {
|
|
2
10
|
type: 'id';
|
|
3
11
|
value: string;
|
|
@@ -52,10 +60,10 @@ type WithOptions = {
|
|
|
52
60
|
[qualifier: string]: { select: string | string[], throughRelation?: string }
|
|
53
61
|
}
|
|
54
62
|
|
|
55
|
-
|
|
63
|
+
export class Spell<T extends typeof Bone, U = InstanceType<T> | Collection<InstanceType<T>> | ResultSet | number | null> extends Promise<U> {
|
|
56
64
|
constructor(Model: T, opts: SpellOptions);
|
|
57
65
|
|
|
58
|
-
select(...names: Array<string | RawSql>): Spell<T, U>;
|
|
66
|
+
select(...names: Array<string | RawSql> | Array<(name: string) => boolean>): Spell<T, U>;
|
|
59
67
|
insert(opts: SetOptions): Spell<T, number>;
|
|
60
68
|
update(opts: SetOptions): Spell<T, number>;
|
|
61
69
|
upsert(opts: SetOptions): Spell<T, number>;
|
|
@@ -103,7 +111,7 @@ declare class Spell<T extends typeof Bone, U = InstanceType<T> | Collection<Inst
|
|
|
103
111
|
toString(): string;
|
|
104
112
|
}
|
|
105
113
|
|
|
106
|
-
type Literal = null | undefined | boolean | number | string | Date | object | ArrayBuffer;
|
|
114
|
+
type Literal = null | undefined | boolean | number | bigint | string | Date | object | ArrayBuffer;
|
|
107
115
|
|
|
108
116
|
type OperatorCondition = {
|
|
109
117
|
[key in '$eq' | '$ne']?: Literal;
|
|
@@ -129,21 +137,15 @@ type InstanceValues<T> = {
|
|
|
129
137
|
[Property in keyof Extract<T, Literal>]?: Extract<T, Literal>[Property]
|
|
130
138
|
}
|
|
131
139
|
|
|
132
|
-
declare class DataType {}
|
|
133
|
-
|
|
134
|
-
export const DataTypes: {
|
|
135
|
-
[key in 'STRING' | 'INTEGER' | 'BIGINT' | 'DATE' | 'BOOLEAN' | 'TEXT' | 'BLOB' | 'JSON' | 'JSONB' | 'BINARY' | 'VARBINARY' ]: DataType;
|
|
136
|
-
};
|
|
137
|
-
|
|
138
140
|
interface AttributeMeta {
|
|
139
|
-
column
|
|
140
|
-
columnType
|
|
141
|
-
allowNull
|
|
142
|
-
defaultValue
|
|
143
|
-
primaryKey
|
|
144
|
-
dataType
|
|
145
|
-
jsType
|
|
146
|
-
type: DataType
|
|
141
|
+
column?: string;
|
|
142
|
+
columnType?: string;
|
|
143
|
+
allowNull?: boolean;
|
|
144
|
+
defaultValue?: Literal;
|
|
145
|
+
primaryKey?: boolean;
|
|
146
|
+
dataType?: string;
|
|
147
|
+
jsType?: Literal;
|
|
148
|
+
type: DataTypes<DataType>;
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
interface RelateOptions {
|
|
@@ -205,15 +207,17 @@ type ResultSet = {
|
|
|
205
207
|
[key: string]: Literal
|
|
206
208
|
};
|
|
207
209
|
|
|
208
|
-
|
|
210
|
+
export class Collection<T extends Bone> extends Array<T> {
|
|
209
211
|
save(): Promise<void>;
|
|
210
212
|
toJSON(): Object[];
|
|
211
213
|
toObject(): Object[];
|
|
212
214
|
}
|
|
213
215
|
|
|
214
216
|
export class Bone {
|
|
217
|
+
static DataTypes: DataType;
|
|
218
|
+
|
|
215
219
|
/**
|
|
216
|
-
*
|
|
220
|
+
* get the connection pool of the driver
|
|
217
221
|
*/
|
|
218
222
|
static pool: Pool;
|
|
219
223
|
|
|
@@ -250,7 +254,7 @@ export class Bone {
|
|
|
250
254
|
/**
|
|
251
255
|
* The attribute definitions of the model.
|
|
252
256
|
*/
|
|
253
|
-
static attributes: { [key: string]: DataType | AttributeMeta };
|
|
257
|
+
static attributes: { [key: string]: DataTypes<DataType> | AttributeMeta };
|
|
254
258
|
|
|
255
259
|
/**
|
|
256
260
|
* The schema info of current model.
|
|
@@ -617,7 +621,7 @@ interface RawQueryOptions {
|
|
|
617
621
|
}
|
|
618
622
|
|
|
619
623
|
export default class Realm {
|
|
620
|
-
Bone: Bone;
|
|
624
|
+
Bone: typeof Bone;
|
|
621
625
|
driver: Driver;
|
|
622
626
|
models: Record<string, Bone>;
|
|
623
627
|
|