leoric 2.3.0 → 2.3.1
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 +13 -0
- package/package.json +1 -1
- package/src/adapters/sequelize.js +9 -4
- package/src/bone.js +18 -3
- package/src/data_types.js +52 -12
- package/src/drivers/abstract/attribute.js +4 -0
- package/src/drivers/abstract/schema.js +5 -2
- package/src/drivers/mysql/attribute.js +11 -2
- package/src/drivers/mysql/schema.js +1 -1
- package/src/drivers/postgres/schema.js +7 -1
- package/src/drivers/sqlite/schema.js +10 -2
- package/types/data_types.d.ts +9 -1
package/History.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
2.3.1 / 2022-03-22
|
|
2
|
+
==================
|
|
3
|
+
|
|
4
|
+
## What's Changed
|
|
5
|
+
* fix: mysql2 Invalid Date compatible by @JimmyDaddy in https://github.com/cyjake/leoric/pull/291
|
|
6
|
+
* fix: order by raw in sequelize mode by @JimmyDaddy in https://github.com/cyjake/leoric/pull/292
|
|
7
|
+
* fix: bulk update query conditions duplicated in sequelize mode by @JimmyDaddy in https://github.com/cyjake/leoric/pull/293
|
|
8
|
+
* fix: bulk destroy query conditions duplicated in sequelize mode by @JimmyDaddy in https://github.com/cyjake/leoric/pull/295
|
|
9
|
+
* fix: drop column if not defined in attributes when alter table by @cyjake in https://github.com/cyjake/leoric/pull/296
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
**Full Changelog**: https://github.com/cyjake/leoric/compare/v2.3.0...v2.3.1
|
|
13
|
+
|
|
1
14
|
2.3.0 / 2022-03-10
|
|
2
15
|
==================
|
|
3
16
|
|
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { setupSingleHook } = require('../setup_hooks');
|
|
4
4
|
const { compose, isPlainObject } = require('../utils');
|
|
5
|
+
const Raw = require('../raw');
|
|
5
6
|
|
|
6
7
|
function translateOptions(spell, options) {
|
|
7
8
|
const { attributes, where, group, order, offset, limit, include, having } = options;
|
|
@@ -18,7 +19,7 @@ function translateOptions(spell, options) {
|
|
|
18
19
|
if (having) spell.$having(having);
|
|
19
20
|
|
|
20
21
|
if (order) {
|
|
21
|
-
if (typeof order === 'string') {
|
|
22
|
+
if (typeof order === 'string' || order instanceof Raw) {
|
|
22
23
|
spell.$order(order);
|
|
23
24
|
} else if (Array.isArray(order) && order.length) {
|
|
24
25
|
if (order.some(item => Array.isArray(item))) {
|
|
@@ -317,10 +318,12 @@ module.exports = Bone => {
|
|
|
317
318
|
}
|
|
318
319
|
|
|
319
320
|
// proxy to class.destroy({ individualHooks=false }) see https://github.com/sequelize/sequelize/blob/4063c2ab627ad57919d5b45cc7755f077a69fa5e/lib/model.js#L2895 before(after)BulkDestroy
|
|
320
|
-
static
|
|
321
|
+
static bulkDestroy(options = {}) {
|
|
321
322
|
const { where, force } = options;
|
|
322
323
|
const spell = this._remove(where || {}, force, { ...options });
|
|
323
|
-
|
|
324
|
+
const transOptions = { ...options };
|
|
325
|
+
delete transOptions.where;
|
|
326
|
+
translateOptions(spell, transOptions);
|
|
324
327
|
return spell;
|
|
325
328
|
}
|
|
326
329
|
|
|
@@ -496,7 +499,9 @@ module.exports = Bone => {
|
|
|
496
499
|
const { where, paranoid = false, validate } = options;
|
|
497
500
|
const whereConditions = where || {};
|
|
498
501
|
const spell = super.update(whereConditions, values, { validate, hooks: false, ...options });
|
|
499
|
-
|
|
502
|
+
const transOptions = { ...options };
|
|
503
|
+
delete transOptions.where;
|
|
504
|
+
translateOptions(spell, transOptions);
|
|
500
505
|
if (!paranoid) return spell.unparanoid;
|
|
501
506
|
return spell;
|
|
502
507
|
}
|
package/src/bone.js
CHANGED
|
@@ -37,10 +37,12 @@ function looseReadonly(props) {
|
|
|
37
37
|
|
|
38
38
|
function compare(attributes, columnMap) {
|
|
39
39
|
const diff = {};
|
|
40
|
+
const columnNames = new Set();
|
|
40
41
|
|
|
41
42
|
for (const name in attributes) {
|
|
42
43
|
const attribute = attributes[name];
|
|
43
44
|
const { columnName } = attribute;
|
|
45
|
+
columnNames.add(columnName);
|
|
44
46
|
|
|
45
47
|
if (!attribute.equals(columnMap[columnName])) {
|
|
46
48
|
diff[name] = {
|
|
@@ -50,6 +52,12 @@ function compare(attributes, columnMap) {
|
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
|
|
55
|
+
for (const columnName in columnMap) {
|
|
56
|
+
if (!columnNames.has(columnName)) {
|
|
57
|
+
diff[columnName] = { remove: true };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
53
61
|
return diff;
|
|
54
62
|
}
|
|
55
63
|
|
|
@@ -254,8 +262,8 @@ class Bone {
|
|
|
254
262
|
|
|
255
263
|
/**
|
|
256
264
|
* get actual update/insert columns to avoid empty insert or update
|
|
257
|
-
* @param {Object} data
|
|
258
|
-
* @returns
|
|
265
|
+
* @param {Object} data
|
|
266
|
+
* @returns
|
|
259
267
|
*/
|
|
260
268
|
static _getColumns(data) {
|
|
261
269
|
if (!Object.keys(data).length) return data;
|
|
@@ -573,7 +581,14 @@ class Bone {
|
|
|
573
581
|
for (const name of Object.keys(attributes)) {
|
|
574
582
|
const attribute = attributes[name];
|
|
575
583
|
// Take advantage of uncast/cast to create new copy of value
|
|
576
|
-
|
|
584
|
+
let value;
|
|
585
|
+
try {
|
|
586
|
+
value = attribute.uncast(this.#raw[name]);
|
|
587
|
+
} catch (error) {
|
|
588
|
+
console.error(error);
|
|
589
|
+
// do not interrupt sync raw
|
|
590
|
+
value = this.#raw[name];
|
|
591
|
+
}
|
|
577
592
|
if (this.#rawSaved[name] !== undefined) {
|
|
578
593
|
this.#rawPrevious[name] = this.#rawSaved[name];
|
|
579
594
|
} else if (this.#rawPrevious[name] === undefined && this.#raw[name] != null) {
|
package/src/data_types.js
CHANGED
|
@@ -17,17 +17,20 @@ class DataType {
|
|
|
17
17
|
const {
|
|
18
18
|
STRING, TEXT,
|
|
19
19
|
DATE, DATEONLY,
|
|
20
|
-
TINYINT, SMALLINT, MEDIUMINT, INTEGER, BIGINT,
|
|
20
|
+
TINYINT, SMALLINT, MEDIUMINT, INTEGER, BIGINT, DECIMAL,
|
|
21
21
|
BOOLEAN,
|
|
22
22
|
BINARY, VARBINARY, BLOB,
|
|
23
23
|
} = this;
|
|
24
|
-
const [ , dataType,
|
|
25
|
-
const
|
|
24
|
+
const [ , dataType, ...matches ] = columnType.match(/(\w+)(?:\((\d+)(?:,(\d+))?\))?/);
|
|
25
|
+
const params = [];
|
|
26
|
+
for (let i = 0; i < matches.length; i++) {
|
|
27
|
+
if (matches[i] != null) params[i] = parseInt(matches[i], 10);
|
|
28
|
+
}
|
|
26
29
|
|
|
27
30
|
switch (dataType) {
|
|
28
31
|
case 'varchar':
|
|
29
32
|
case 'char':
|
|
30
|
-
return new STRING(
|
|
33
|
+
return new STRING(...params);
|
|
31
34
|
// longtext is only for MySQL
|
|
32
35
|
case 'longtext':
|
|
33
36
|
return new TEXT('long');
|
|
@@ -40,30 +43,31 @@ class DataType {
|
|
|
40
43
|
case 'datetime':
|
|
41
44
|
case 'timestamp':
|
|
42
45
|
// new DATE(precision)
|
|
43
|
-
return new DATE(
|
|
46
|
+
return new DATE(...params);
|
|
44
47
|
case 'decimal':
|
|
48
|
+
return new DECIMAL(...params);
|
|
45
49
|
case 'int':
|
|
46
50
|
case 'integer':
|
|
47
51
|
case 'numeric':
|
|
48
|
-
return new INTEGER(
|
|
52
|
+
return new INTEGER(...params);
|
|
49
53
|
case 'mediumint':
|
|
50
|
-
return new MEDIUMINT(
|
|
54
|
+
return new MEDIUMINT(...params);
|
|
51
55
|
case 'smallint':
|
|
52
|
-
return new SMALLINT(
|
|
56
|
+
return new SMALLINT(...params);
|
|
53
57
|
case 'tinyint':
|
|
54
|
-
return new TINYINT(
|
|
58
|
+
return new TINYINT(...params);
|
|
55
59
|
case 'bigint':
|
|
56
|
-
return new BIGINT(
|
|
60
|
+
return new BIGINT(...params);
|
|
57
61
|
case 'boolean':
|
|
58
62
|
return new BOOLEAN();
|
|
59
63
|
// mysql only
|
|
60
64
|
case 'binary':
|
|
61
65
|
// postgres only
|
|
62
66
|
case 'bytea':
|
|
63
|
-
return new BINARY(
|
|
67
|
+
return new BINARY(...params);
|
|
64
68
|
// mysql only
|
|
65
69
|
case 'varbinary':
|
|
66
|
-
return new VARBINARY(
|
|
70
|
+
return new VARBINARY(...params);
|
|
67
71
|
case 'longblob':
|
|
68
72
|
return new BLOB('long');
|
|
69
73
|
case 'mediumblob':
|
|
@@ -273,6 +277,41 @@ class BIGINT extends INTEGER {
|
|
|
273
277
|
}
|
|
274
278
|
}
|
|
275
279
|
|
|
280
|
+
/**
|
|
281
|
+
* fixed-point decimal types
|
|
282
|
+
* @example
|
|
283
|
+
* DECIMAL
|
|
284
|
+
* DECIMAL.UNSIGNED
|
|
285
|
+
* DECIMAL(5, 2)
|
|
286
|
+
* @param {number} precision
|
|
287
|
+
* @param {number} scale
|
|
288
|
+
* - https://dev.mysql.com/doc/refman/8.0/en/fixed-point-types.html
|
|
289
|
+
*/
|
|
290
|
+
class DECIMAL extends INTEGER {
|
|
291
|
+
constructor(precision, scale) {
|
|
292
|
+
super();
|
|
293
|
+
this.dataType = 'decimal';
|
|
294
|
+
this.precision = precision;
|
|
295
|
+
this.scale = scale;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
toSqlString() {
|
|
299
|
+
const { precision, scale, unsigned, zerofill } = this;
|
|
300
|
+
const dataType = this.dataType.toUpperCase();
|
|
301
|
+
const chunks = [];
|
|
302
|
+
if (precision > 0 && scale >= 0) {
|
|
303
|
+
chunks.push(`${dataType}(${precision},${scale})`);
|
|
304
|
+
} else if (precision > 0) {
|
|
305
|
+
chunks.push(`${dataType}(${precision})`);
|
|
306
|
+
} else {
|
|
307
|
+
chunks.push(dataType);
|
|
308
|
+
}
|
|
309
|
+
if (unsigned) chunks.push('UNSIGNED');
|
|
310
|
+
if (zerofill) chunks.push('ZEROFILL');
|
|
311
|
+
return chunks.join(' ');
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
276
315
|
const rDateFormat = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:[,.]\d{3,6})$/;
|
|
277
316
|
|
|
278
317
|
class DATE extends DataType {
|
|
@@ -494,6 +533,7 @@ const DataTypes = {
|
|
|
494
533
|
MEDIUMINT,
|
|
495
534
|
INTEGER,
|
|
496
535
|
BIGINT,
|
|
536
|
+
DECIMAL,
|
|
497
537
|
DATE,
|
|
498
538
|
DATEONLY,
|
|
499
539
|
BOOLEAN,
|
|
@@ -66,6 +66,8 @@ function createType(DataTypes, params) {
|
|
|
66
66
|
switch (type.constructor.name) {
|
|
67
67
|
case 'DATE':
|
|
68
68
|
return new DataType(type.precision, type.timezone);
|
|
69
|
+
case 'DECIMAL':
|
|
70
|
+
return new DataType(type.precision, type.scale);
|
|
69
71
|
case 'TINYINT':
|
|
70
72
|
case 'SMALLINT':
|
|
71
73
|
case 'MEDIUMINT':
|
|
@@ -73,6 +75,8 @@ function createType(DataTypes, params) {
|
|
|
73
75
|
case 'BIGINT':
|
|
74
76
|
case 'BINARY':
|
|
75
77
|
case 'VARBINARY':
|
|
78
|
+
case 'CHAR':
|
|
79
|
+
case 'VARCHAR':
|
|
76
80
|
return new DataType(type.length);
|
|
77
81
|
default:
|
|
78
82
|
return new DataType();
|
|
@@ -19,9 +19,12 @@ module.exports = {
|
|
|
19
19
|
const chunks = [ `ALTER TABLE ${escapeId(table)}` ];
|
|
20
20
|
|
|
21
21
|
const actions = Object.keys(attributes).map(name => {
|
|
22
|
-
const
|
|
22
|
+
const options = attributes[name];
|
|
23
|
+
// { [columnName]: { remove: true } }
|
|
24
|
+
if (options.remove) return `DROP COLUMN ${escapeId(name)}`;
|
|
25
|
+
const attribute = new Attribute(name, options);
|
|
23
26
|
return [
|
|
24
|
-
|
|
27
|
+
options.modify ? 'MODIFY COLUMN' : 'ADD COLUMN',
|
|
25
28
|
attribute.toSqlString(),
|
|
26
29
|
].join(' ');
|
|
27
30
|
});
|
|
@@ -15,8 +15,13 @@ class MysqlAttribute extends Attribute {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
toSqlString() {
|
|
18
|
-
const {
|
|
19
|
-
|
|
18
|
+
const {
|
|
19
|
+
columnName,
|
|
20
|
+
type, columnType,
|
|
21
|
+
allowNull, defaultValue,
|
|
22
|
+
primaryKey,
|
|
23
|
+
comment,
|
|
24
|
+
} = this;
|
|
20
25
|
|
|
21
26
|
const chunks = [
|
|
22
27
|
escapeId(columnName),
|
|
@@ -33,6 +38,10 @@ class MysqlAttribute extends Attribute {
|
|
|
33
38
|
chunks.push(`DEFAULT ${escape(defaultValue)}`);
|
|
34
39
|
}
|
|
35
40
|
|
|
41
|
+
if (typeof comment === 'string') {
|
|
42
|
+
chunks.push(`COMMENT ${escape(comment)}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
return chunks.join(' ');
|
|
37
46
|
}
|
|
38
47
|
}
|
|
@@ -38,7 +38,7 @@ module.exports = {
|
|
|
38
38
|
columns.push({
|
|
39
39
|
columnName: row.column_name,
|
|
40
40
|
columnType: row.column_type,
|
|
41
|
-
|
|
41
|
+
comment: row.column_comment,
|
|
42
42
|
defaultValue: row.column_default,
|
|
43
43
|
dataType: row.data_type,
|
|
44
44
|
allowNull: row.is_nullable === 'YES',
|
|
@@ -28,6 +28,10 @@ function formatAddColumn(driver, columnName, attribute) {
|
|
|
28
28
|
return `ADD COLUMN ${attribute.toSqlString()}`;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
function formatDropColumn(driver, columnName) {
|
|
32
|
+
return `DROP COLUMN ${driver.escapeId(columnName)}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
31
35
|
module.exports = {
|
|
32
36
|
...schema,
|
|
33
37
|
|
|
@@ -85,7 +89,9 @@ module.exports = {
|
|
|
85
89
|
const { escapeId } = this;
|
|
86
90
|
const chunks = [ `ALTER TABLE ${escapeId(table)}` ];
|
|
87
91
|
const actions = Object.keys(changes).map(name => {
|
|
88
|
-
const
|
|
92
|
+
const options = changes[name];
|
|
93
|
+
if (options.remove) return formatDropColumn(this, name);
|
|
94
|
+
const attribute = new Attribute(name, options);
|
|
89
95
|
const { columnName } = attribute;;
|
|
90
96
|
return attribute.modify
|
|
91
97
|
? formatAlterColumns(this, columnName, attribute).join(', ')
|
|
@@ -108,7 +108,8 @@ module.exports = {
|
|
|
108
108
|
for (let i = 0; i < tables.length; i++) {
|
|
109
109
|
const table = tables[i];
|
|
110
110
|
const { rows } = results[i];
|
|
111
|
-
const columns = rows.map(
|
|
111
|
+
const columns = rows.map(row => {
|
|
112
|
+
const { name, type, notnull, dflt_value, pk } = row;
|
|
112
113
|
const columnType = type.toLowerCase();
|
|
113
114
|
const [, dataType, precision ] = columnType.match(rColumnType);
|
|
114
115
|
const primaryKey = pk === 1;
|
|
@@ -145,6 +146,8 @@ module.exports = {
|
|
|
145
146
|
const { escapeId } = this;
|
|
146
147
|
const chunks = [ `ALTER TABLE ${escapeId(table)}` ];
|
|
147
148
|
const attributes = Object.keys(changes).map(name => {
|
|
149
|
+
const options = changes[name];
|
|
150
|
+
if (options.remove) return { columnName: name, remove: true };
|
|
148
151
|
return new Attribute(name, changes[name]);
|
|
149
152
|
});
|
|
150
153
|
|
|
@@ -157,7 +160,12 @@ module.exports = {
|
|
|
157
160
|
// SQLite can only add one column a time
|
|
158
161
|
// - https://www.sqlite.org/lang_altertable.html
|
|
159
162
|
for (const attribute of attributes) {
|
|
160
|
-
|
|
163
|
+
if (attribute.remove) {
|
|
164
|
+
const { columnName } = attribute;
|
|
165
|
+
await this.query(chunks.concat(`DROP COLUMN ${this.escapeId(columnName)}`).join(' '));
|
|
166
|
+
} else {
|
|
167
|
+
await this.query(chunks.concat(`ADD COLUMN ${attribute.toSqlString()}`).join(' '));
|
|
168
|
+
}
|
|
161
169
|
}
|
|
162
170
|
},
|
|
163
171
|
|
package/types/data_types.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export default class DataType {
|
|
|
11
11
|
static STRING: typeof STRING & INVOKABLE<STRING>;
|
|
12
12
|
static INTEGER: typeof INTEGER & INVOKABLE<INTEGER>;
|
|
13
13
|
static BIGINT: typeof BIGINT & INVOKABLE<BIGINT>;
|
|
14
|
+
static DECIMAL: typeof DECIMAL & INVOKABLE<DECIMAL>;
|
|
14
15
|
static TEXT: typeof TEXT & INVOKABLE<TEXT>;
|
|
15
16
|
static BLOB: typeof BLOB & INVOKABLE<BLOB>;
|
|
16
17
|
static JSON: typeof JSON & INVOKABLE<JSON>;
|
|
@@ -31,7 +32,7 @@ declare class STRING extends DataType {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
declare class INTEGER extends DataType {
|
|
34
|
-
dataType: 'integer' | 'bigint';
|
|
35
|
+
dataType: 'integer' | 'bigint' | 'decimal';
|
|
35
36
|
length: number;
|
|
36
37
|
constructor(length: number);
|
|
37
38
|
get UNSIGNED(): this;
|
|
@@ -42,6 +43,13 @@ declare class BIGINT extends INTEGER {
|
|
|
42
43
|
dataType: 'bigint';
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
declare class DECIMAL extends INTEGER {
|
|
47
|
+
dataType: 'decimal';
|
|
48
|
+
precision: number;
|
|
49
|
+
scale: number;
|
|
50
|
+
constructor(precision: number, scale: number);
|
|
51
|
+
}
|
|
52
|
+
|
|
45
53
|
declare class TEXT extends DataType {
|
|
46
54
|
dataType: 'text';
|
|
47
55
|
length: LENGTH_VARIANTS;
|