leoric 2.3.2 → 2.5.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/History.md +31 -0
- package/Readme.md +1 -1
- package/index.js +14 -0
- package/package.json +3 -1
- package/src/bone.js +11 -11
- package/src/constants.js +8 -1
- package/src/data_types.js +32 -32
- package/src/drivers/abstract/attribute.js +1 -1
- package/src/drivers/abstract/index.js +201 -1
- package/src/drivers/abstract/spellbook.js +403 -412
- package/src/drivers/index.js +15 -4
- package/src/drivers/mysql/index.js +101 -10
- package/src/drivers/mysql/spellbook.js +13 -11
- package/src/drivers/postgres/data_types.js +2 -2
- package/src/drivers/postgres/index.js +103 -109
- package/src/drivers/postgres/spellbook.js +9 -9
- package/src/drivers/postgres/sqlstring.js +124 -0
- package/src/drivers/sqlite/data_types.js +9 -9
- package/src/drivers/sqlite/index.js +124 -13
- package/src/drivers/sqlite/spellbook.js +6 -6
- package/src/drivers/sqlite/sqlstring.js +88 -0
- package/src/hint.js +2 -1
- package/src/realm.js +13 -5
- package/src/spell.js +2 -4
- package/src/utils/invokable.js +3 -0
- package/types/data_types.d.ts +35 -32
- package/types/hint.d.ts +96 -0
- package/types/index.d.ts +266 -26
- package/src/drivers/abstract/schema.js +0 -143
- package/src/drivers/mysql/schema.js +0 -98
- package/src/drivers/postgres/schema.js +0 -125
- package/src/drivers/sqlite/schema.js +0 -211
package/History.md
CHANGED
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
2.5.0 / 2022-05-13
|
|
2
|
+
==================
|
|
3
|
+
|
|
4
|
+
## What's Changed
|
|
5
|
+
* feat: support disconnect and fix timestamps init by @JimmyDaddy in https://github.com/cyjake/leoric/pull/313
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
**Full Changelog**: https://github.com/cyjake/leoric/compare/v2.4.1...v2.5.0
|
|
9
|
+
|
|
10
|
+
2.4.1 / 2022-04-27
|
|
11
|
+
==================
|
|
12
|
+
|
|
13
|
+
## What's Changed
|
|
14
|
+
* fix: realm.Bone.DataTypes should be invokable, Invokable.TYPE.toSqlString() get wrong default length(1), DataType definitions by @JimmyDaddy in https://github.com/cyjake/leoric/pull/307
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
**Full Changelog**: https://github.com/cyjake/leoric/compare/v2.4.0...v2.4.1
|
|
18
|
+
|
|
19
|
+
2.4.0 / 2022-04-24
|
|
20
|
+
==================
|
|
21
|
+
|
|
22
|
+
## What's Changed
|
|
23
|
+
* feat: support custom driver by @JimmyDaddy in https://github.com/cyjake/leoric/pull/304
|
|
24
|
+
* chore: update build status badge by @snapre in https://github.com/cyjake/leoric/pull/305
|
|
25
|
+
* feat: export more ts type definitions and use deep-equal module by @JimmyDaddy in https://github.com/cyjake/leoric/pull/306
|
|
26
|
+
|
|
27
|
+
## New Contributors
|
|
28
|
+
* @snapre made their first contribution in https://github.com/cyjake/leoric/pull/305
|
|
29
|
+
|
|
30
|
+
**Full Changelog**: https://github.com/cyjake/leoric/compare/v2.3.2...v2.4.0
|
|
31
|
+
|
|
1
32
|
2.3.2 / 2022-04-15
|
|
2
33
|
==================
|
|
3
34
|
|
package/Readme.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://packagequality.com/#?package=leoric)
|
|
4
4
|
[](https://www.npmjs.com/package/leoric)
|
|
5
5
|
[](https://www.npmjs.com/package/leoric)
|
|
6
|
-
[](https://github.com/cyjake/leoric/actions/workflows/nodejs.yml)
|
|
7
7
|
[](https://codecov.io/gh/cyjake/leoric)
|
|
8
8
|
|
|
9
9
|
Leoric is an object-relational mapping for Node.js, which is heavily influenced by Active Record of Ruby on Rails. See the [documentation](https://leoric.js.org) for detail.
|
package/index.js
CHANGED
|
@@ -12,6 +12,8 @@ const { heresql } = require('./src/utils/string');
|
|
|
12
12
|
const Hint = require('./src/hint');
|
|
13
13
|
const Realm = require('./src/realm');
|
|
14
14
|
const Decorators = require('./src/decorators');
|
|
15
|
+
const Raw = require('./src/raw');
|
|
16
|
+
const { MysqlDriver, PostgresDriver, SqliteDriver, AbstractDriver } = require('./src/drivers');
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* @typedef {Object} RawSql
|
|
@@ -37,10 +39,17 @@ const connect = async function connect(opts) {
|
|
|
37
39
|
return realm;
|
|
38
40
|
};
|
|
39
41
|
|
|
42
|
+
const disconnect = async function disconnect(realm, ...args) {
|
|
43
|
+
if (realm instanceof Realm && realm.connected) {
|
|
44
|
+
return await realm.disconnect(...args);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
40
48
|
Object.assign(Realm.prototype, migrations, { DataTypes });
|
|
41
49
|
Object.assign(Realm, {
|
|
42
50
|
default: Realm,
|
|
43
51
|
connect,
|
|
52
|
+
disconnect,
|
|
44
53
|
Bone,
|
|
45
54
|
Collection,
|
|
46
55
|
DataTypes,
|
|
@@ -50,6 +59,11 @@ Object.assign(Realm, {
|
|
|
50
59
|
heresql,
|
|
51
60
|
...Hint,
|
|
52
61
|
...Decorators,
|
|
62
|
+
MysqlDriver,
|
|
63
|
+
PostgresDriver,
|
|
64
|
+
SqliteDriver,
|
|
65
|
+
AbstractDriver,
|
|
66
|
+
Raw,
|
|
53
67
|
});
|
|
54
68
|
|
|
55
69
|
module.exports = Realm;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leoric",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "JavaScript Object-relational mapping alchemy",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"test:mysql2": "./test/start.sh test/integration/mysql2.test.js",
|
|
22
22
|
"test:postgres": "./test/start.sh test/integration/postgres.test.js",
|
|
23
23
|
"test:sqlite": "./test/start.sh test/integration/sqlite.test.js",
|
|
24
|
+
"test:custom": "./test/start.sh test/integration/custom.test.js",
|
|
24
25
|
"test:sqlcipher": "./test/start.sh test/integration/sqlcipher.test.js",
|
|
25
26
|
"test:dts": "./test/start.sh dts",
|
|
26
27
|
"test:coverage": "nyc ./test/start.sh && nyc report --reporter=lcov",
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
},
|
|
50
51
|
"dependencies": {
|
|
51
52
|
"debug": "^3.1.0",
|
|
53
|
+
"deep-equal": "^2.0.5",
|
|
52
54
|
"heredoc": "^1.3.1",
|
|
53
55
|
"pluralize": "^7.0.0",
|
|
54
56
|
"reflect-metadata": "^0.1.13",
|
package/src/bone.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
const util = require('util');
|
|
8
|
+
const deepEqual = require('deep-equal');
|
|
8
9
|
const pluralize = require('pluralize');
|
|
9
10
|
const { executeValidator, LeoricValidateError } = require('./validator');
|
|
10
11
|
require('reflect-metadata');
|
|
@@ -19,6 +20,7 @@ const {
|
|
|
19
20
|
TIMESTAMP_NAMES,
|
|
20
21
|
LEGACY_TIMESTAMP_COLUMN_MAP,
|
|
21
22
|
ASSOCIATE_METADATA_MAP,
|
|
23
|
+
TIMESTAMP_ATTRIBUTE_NAMES,
|
|
22
24
|
} = require('./constants');
|
|
23
25
|
|
|
24
26
|
const columnAttributesKey = Symbol('leoric#columns');
|
|
@@ -137,6 +139,8 @@ function valuesValidate(values, attributes, ctx) {
|
|
|
137
139
|
*/
|
|
138
140
|
class Bone {
|
|
139
141
|
|
|
142
|
+
static DataTypes = DataTypes.invokable;
|
|
143
|
+
|
|
140
144
|
// private variables
|
|
141
145
|
#raw = {};
|
|
142
146
|
#rawSaved = {};
|
|
@@ -380,7 +384,7 @@ class Bone {
|
|
|
380
384
|
if (this.#rawUnset.has(name) || !this.hasAttribute(name)) return false;
|
|
381
385
|
const value = this.attribute(name);
|
|
382
386
|
const valueWas = this.attributeWas(name);
|
|
383
|
-
return !
|
|
387
|
+
return !deepEqual(value, valueWas);
|
|
384
388
|
}
|
|
385
389
|
|
|
386
390
|
/**
|
|
@@ -412,7 +416,7 @@ class Bone {
|
|
|
412
416
|
if (this.#rawUnset.has(name) || this.#rawPrevious[name] === undefined || !this.hasAttribute(name)) return {};
|
|
413
417
|
const value = this.attribute(name);
|
|
414
418
|
const valueWas = this.#rawPrevious[name] == null ? null : this.#rawPrevious[name];
|
|
415
|
-
if (
|
|
419
|
+
if (deepEqual(value, valueWas)) return {};
|
|
416
420
|
return { [name]: [ valueWas, value ] };
|
|
417
421
|
}
|
|
418
422
|
const result = {};
|
|
@@ -420,7 +424,7 @@ class Bone {
|
|
|
420
424
|
if (this.#rawUnset.has(attrKey) || this.#rawPrevious[attrKey] === undefined) continue;
|
|
421
425
|
const value = this.attribute(attrKey);
|
|
422
426
|
const valueWas = this.#rawPrevious[attrKey] == null ? null : this.#rawPrevious[attrKey];
|
|
423
|
-
if (!
|
|
427
|
+
if (!deepEqual(value, valueWas)) result[attrKey] = [ valueWas, value ];
|
|
424
428
|
}
|
|
425
429
|
return result;
|
|
426
430
|
}
|
|
@@ -439,7 +443,7 @@ class Bone {
|
|
|
439
443
|
if (this.#rawUnset.has(name) || !this.hasAttribute(name)) return {};
|
|
440
444
|
const value = this.attribute(name);
|
|
441
445
|
const valueWas = this.attributeWas(name);
|
|
442
|
-
if (
|
|
446
|
+
if (deepEqual(value, valueWas)) return {};
|
|
443
447
|
return { [name]: [ valueWas, value ] };
|
|
444
448
|
}
|
|
445
449
|
const result = {};
|
|
@@ -448,7 +452,7 @@ class Bone {
|
|
|
448
452
|
const value = this.attribute(attrKey);
|
|
449
453
|
const valueWas = this.attributeWas(attrKey);
|
|
450
454
|
|
|
451
|
-
if (!
|
|
455
|
+
if (!deepEqual(value, valueWas)) {
|
|
452
456
|
result[attrKey] = [ valueWas, value ];
|
|
453
457
|
}
|
|
454
458
|
}
|
|
@@ -996,7 +1000,7 @@ class Bone {
|
|
|
996
1000
|
const attribute = new Attribute(name, attributes[name], options.define);
|
|
997
1001
|
attributeMap[attribute.columnName] = attribute;
|
|
998
1002
|
attributes[name] = attribute;
|
|
999
|
-
if (
|
|
1003
|
+
if (TIMESTAMP_ATTRIBUTE_NAMES.includes(name)) {
|
|
1000
1004
|
const { columnName } = attribute;
|
|
1001
1005
|
const legacyColumnName = LEGACY_TIMESTAMP_COLUMN_MAP[columnName];
|
|
1002
1006
|
if (!columnMap[columnName] && legacyColumnName && columnMap[legacyColumnName]) {
|
|
@@ -1560,9 +1564,7 @@ class Bone {
|
|
|
1560
1564
|
}
|
|
1561
1565
|
|
|
1562
1566
|
static query(spell) {
|
|
1563
|
-
|
|
1564
|
-
const query = { sql, nestTables: spell.command === 'select' };
|
|
1565
|
-
return this.driver.query(query, values, spell);
|
|
1567
|
+
return this.driver.cast(spell);
|
|
1566
1568
|
}
|
|
1567
1569
|
|
|
1568
1570
|
static async transaction(callback) {
|
|
@@ -1713,6 +1715,4 @@ for (const getter of Spell_getters) {
|
|
|
1713
1715
|
});
|
|
1714
1716
|
}
|
|
1715
1717
|
|
|
1716
|
-
Object.assign(Bone, { DataTypes });
|
|
1717
|
-
|
|
1718
1718
|
module.exports = Bone;
|
package/src/constants.js
CHANGED
|
@@ -20,6 +20,12 @@ const LEGACY_TIMESTAMP_COLUMN_MAP = {
|
|
|
20
20
|
deleted_at: 'gmt_deleted',
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
const TIMESTAMP_ATTRIBUTE_NAMES = [
|
|
24
|
+
'createdAt', 'updatedAt', 'deletedAt',
|
|
25
|
+
'gmtCreate', 'gmtModified', 'gmtDeleted',
|
|
26
|
+
'created_at', 'updated_at', 'deleted_at',
|
|
27
|
+
'gmt_create', 'gmt_modified', 'gmt_deleted',
|
|
28
|
+
];
|
|
23
29
|
const TIMESTAMP_NAMES = [ 'createdAt', 'updatedAt', 'deletedAt' ];
|
|
24
30
|
|
|
25
31
|
const ASSOCIATE_METADATA_MAP = {
|
|
@@ -33,5 +39,6 @@ module.exports = {
|
|
|
33
39
|
LEGACY_TIMESTAMP_MAP,
|
|
34
40
|
TIMESTAMP_NAMES,
|
|
35
41
|
LEGACY_TIMESTAMP_COLUMN_MAP,
|
|
36
|
-
ASSOCIATE_METADATA_MAP
|
|
42
|
+
ASSOCIATE_METADATA_MAP,
|
|
43
|
+
TIMESTAMP_ATTRIBUTE_NAMES
|
|
37
44
|
};
|
package/src/data_types.js
CHANGED
|
@@ -110,20 +110,20 @@ class DataType {
|
|
|
110
110
|
* STRING
|
|
111
111
|
* STRING(127)
|
|
112
112
|
* STRING.BINARY
|
|
113
|
-
* @param {number}
|
|
113
|
+
* @param {number} dataLength
|
|
114
114
|
*/
|
|
115
115
|
class STRING extends DataType {
|
|
116
|
-
constructor(
|
|
116
|
+
constructor(dataLength = 255) {
|
|
117
117
|
super();
|
|
118
118
|
this.dataType = 'varchar';
|
|
119
|
-
this.
|
|
119
|
+
this.dataLength = dataLength;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
toSqlString() {
|
|
123
|
-
const {
|
|
123
|
+
const { dataLength } = this;
|
|
124
124
|
const dataType = this.dataType.toUpperCase();
|
|
125
125
|
const chunks = [];
|
|
126
|
-
chunks.push(
|
|
126
|
+
chunks.push(dataLength > 0 ? `${dataType}(${dataLength})` : dataType);
|
|
127
127
|
return chunks.join(' ');
|
|
128
128
|
}
|
|
129
129
|
|
|
@@ -134,17 +134,17 @@ class STRING extends DataType {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
class BINARY extends DataType {
|
|
137
|
-
constructor(
|
|
137
|
+
constructor(dataLength = 255) {
|
|
138
138
|
super();
|
|
139
|
-
this.
|
|
139
|
+
this.dataLength = dataLength;
|
|
140
140
|
this.dataType = 'binary';
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
toSqlString() {
|
|
144
|
-
const {
|
|
144
|
+
const { dataLength } = this;
|
|
145
145
|
const dataType = this.dataType.toUpperCase();
|
|
146
146
|
const chunks = [];
|
|
147
|
-
chunks.push(
|
|
147
|
+
chunks.push(dataLength > 0 ? `${dataType}(${dataLength})` : dataType);
|
|
148
148
|
return chunks.join(' ');
|
|
149
149
|
}
|
|
150
150
|
|
|
@@ -156,8 +156,8 @@ class BINARY extends DataType {
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
class VARBINARY extends BINARY {
|
|
159
|
-
constructor(
|
|
160
|
-
super(
|
|
159
|
+
constructor(dataLength) {
|
|
160
|
+
super(dataLength);
|
|
161
161
|
this.dataType = 'varbinary';
|
|
162
162
|
}
|
|
163
163
|
}
|
|
@@ -170,12 +170,12 @@ class VARBINARY extends BINARY {
|
|
|
170
170
|
* INTEGER.UNSIGNED
|
|
171
171
|
* INTEGER.UNSIGNED.ZEROFILL
|
|
172
172
|
* INTEGER(10)
|
|
173
|
-
* @param {number}
|
|
173
|
+
* @param {number} dataLength
|
|
174
174
|
*/
|
|
175
175
|
class INTEGER extends DataType {
|
|
176
|
-
constructor(
|
|
176
|
+
constructor(dataLength) {
|
|
177
177
|
super();
|
|
178
|
-
this.
|
|
178
|
+
this.dataLength = dataLength;
|
|
179
179
|
this.dataType = 'integer';
|
|
180
180
|
}
|
|
181
181
|
|
|
@@ -190,10 +190,10 @@ class INTEGER extends DataType {
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
toSqlString() {
|
|
193
|
-
const {
|
|
193
|
+
const { dataLength, unsigned, zerofill } = this;
|
|
194
194
|
const dataType = this.dataType.toUpperCase();
|
|
195
195
|
const chunks = [];
|
|
196
|
-
chunks.push(
|
|
196
|
+
chunks.push(dataLength > 0 ? `${dataType}(${dataLength})` : dataType);
|
|
197
197
|
if (unsigned) chunks.push('UNSIGNED');
|
|
198
198
|
if (zerofill) chunks.push('ZEROFILL');
|
|
199
199
|
return chunks.join(' ');
|
|
@@ -222,11 +222,11 @@ class INTEGER extends DataType {
|
|
|
222
222
|
* TINYINT
|
|
223
223
|
* TINYINT.UNSIGNED
|
|
224
224
|
* TINYINT(1)
|
|
225
|
-
* @param {number}
|
|
225
|
+
* @param {number} dataLength
|
|
226
226
|
*/
|
|
227
227
|
class TINYINT extends INTEGER {
|
|
228
|
-
constructor(
|
|
229
|
-
super(
|
|
228
|
+
constructor(dataLength) {
|
|
229
|
+
super(dataLength);
|
|
230
230
|
this.dataType = 'tinyint';
|
|
231
231
|
}
|
|
232
232
|
}
|
|
@@ -237,11 +237,11 @@ class TINYINT extends INTEGER {
|
|
|
237
237
|
* SMALLINT
|
|
238
238
|
* SMALLINT.UNSIGNED
|
|
239
239
|
* SMALLINT(2)
|
|
240
|
-
* @param {number}
|
|
240
|
+
* @param {number} dataLength
|
|
241
241
|
*/
|
|
242
242
|
class SMALLINT extends INTEGER {
|
|
243
|
-
constructor(
|
|
244
|
-
super(
|
|
243
|
+
constructor(dataLength) {
|
|
244
|
+
super(dataLength);
|
|
245
245
|
this.dataType = 'smallint';
|
|
246
246
|
}
|
|
247
247
|
}
|
|
@@ -252,11 +252,11 @@ class SMALLINT extends INTEGER {
|
|
|
252
252
|
* MEDIUMINT
|
|
253
253
|
* MEDIUMINT.UNSIGNED
|
|
254
254
|
* MEDIUMINT(3)
|
|
255
|
-
* @param {number}
|
|
255
|
+
* @param {number} dataLength
|
|
256
256
|
*/
|
|
257
257
|
class MEDIUMINT extends INTEGER {
|
|
258
|
-
constructor(
|
|
259
|
-
super(
|
|
258
|
+
constructor(dataLength) {
|
|
259
|
+
super(dataLength);
|
|
260
260
|
this.dataType = 'mediumint';
|
|
261
261
|
}
|
|
262
262
|
}
|
|
@@ -268,11 +268,11 @@ class MEDIUMINT extends INTEGER {
|
|
|
268
268
|
* BIGINT
|
|
269
269
|
* BIGINT.UNSIGNED
|
|
270
270
|
* BIGINT(8)
|
|
271
|
-
* @param {number}
|
|
271
|
+
* @param {number} dataLength
|
|
272
272
|
*/
|
|
273
273
|
class BIGINT extends INTEGER {
|
|
274
|
-
constructor(
|
|
275
|
-
super(
|
|
274
|
+
constructor(dataLength) {
|
|
275
|
+
super(dataLength);
|
|
276
276
|
this.dataType = 'bigint';
|
|
277
277
|
}
|
|
278
278
|
}
|
|
@@ -442,11 +442,11 @@ class TEXT extends DataType {
|
|
|
442
442
|
}
|
|
443
443
|
super();
|
|
444
444
|
this.dataType = 'text';
|
|
445
|
-
this.
|
|
445
|
+
this.dataLength = length;
|
|
446
446
|
}
|
|
447
447
|
|
|
448
448
|
toSqlString() {
|
|
449
|
-
return [ this.
|
|
449
|
+
return [ this.dataLength, this.dataType ].join('').toUpperCase();
|
|
450
450
|
}
|
|
451
451
|
}
|
|
452
452
|
|
|
@@ -457,11 +457,11 @@ class BLOB extends DataType {
|
|
|
457
457
|
}
|
|
458
458
|
super();
|
|
459
459
|
this.dataType = 'blob';
|
|
460
|
-
this.
|
|
460
|
+
this.dataLength = length;
|
|
461
461
|
}
|
|
462
462
|
|
|
463
463
|
toSqlString() {
|
|
464
|
-
return [ this.
|
|
464
|
+
return [ this.dataLength, this.dataType ].join('').toUpperCase();
|
|
465
465
|
}
|
|
466
466
|
|
|
467
467
|
cast(value) {
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const SqlString = require('sqlstring');
|
|
4
|
+
const debug = require('debug')('leoric');
|
|
5
|
+
|
|
3
6
|
const Logger = require('./logger');
|
|
7
|
+
const Attribute = require('./attribute');
|
|
8
|
+
const DataTypes = require('../../data_types');
|
|
9
|
+
const Spellbook = require('./spellbook');
|
|
10
|
+
const { heresql, camelCase } = require('../../utils/string');
|
|
4
11
|
|
|
5
12
|
/**
|
|
6
13
|
* Migration methods
|
|
@@ -8,11 +15,204 @@ const Logger = require('./logger');
|
|
|
8
15
|
* - https://dev.mysql.com/doc/refman/8.0/en/alter-table.html
|
|
9
16
|
*/
|
|
10
17
|
|
|
11
|
-
|
|
18
|
+
class AbstractDriver {
|
|
19
|
+
|
|
20
|
+
// define static properties as this way IDE will prompt
|
|
21
|
+
static Spellbook = Spellbook;
|
|
22
|
+
static Attribute = Attribute;
|
|
23
|
+
static DataTypes = DataTypes;
|
|
24
|
+
|
|
12
25
|
constructor(opts = {}) {
|
|
13
26
|
const { logger } = opts;
|
|
14
27
|
this.logger = logger instanceof Logger ? logger : new Logger(logger);
|
|
15
28
|
this.idleTimeout = opts.idleTimeout || 60;
|
|
16
29
|
this.options = opts;
|
|
30
|
+
this.Attribute = this.constructor.Attribute;
|
|
31
|
+
this.DataTypes = this.constructor.DataTypes;
|
|
32
|
+
this.spellbook = new this.constructor.Spellbook();
|
|
33
|
+
this.escape = SqlString.escape;
|
|
34
|
+
this.escapeId = SqlString.escapeId;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* query with spell
|
|
39
|
+
* @param {Spell} spell
|
|
40
|
+
* @returns
|
|
41
|
+
*/
|
|
42
|
+
async cast(spell) {
|
|
43
|
+
const { sql, values } = this.format(spell);
|
|
44
|
+
const query = { sql, nestTables: spell.command === 'select' };
|
|
45
|
+
return await this.query(query, values, spell);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* raw query
|
|
50
|
+
* @param {object|string} query
|
|
51
|
+
* @param {object | array} values
|
|
52
|
+
* @param {object} opts
|
|
53
|
+
*/
|
|
54
|
+
async query(query, values, opts) {
|
|
55
|
+
throw new Error('unimplemented!');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* disconnect manually
|
|
60
|
+
* @param {Function} callback
|
|
61
|
+
*/
|
|
62
|
+
async disconnect(callback) {
|
|
63
|
+
debug('[disconnect] called');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get dialect() {
|
|
67
|
+
return camelCase(this.constructor.name.replace('Driver', ''));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* use spellbook to format spell
|
|
72
|
+
* @param {Spell} spell
|
|
73
|
+
* @returns
|
|
74
|
+
*/
|
|
75
|
+
format(spell) {
|
|
76
|
+
return this.spellbook.format(spell);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async createTable(table, attributes) {
|
|
80
|
+
const { escapeId } = this;
|
|
81
|
+
const chunks = [ `CREATE TABLE ${escapeId(table)}` ];
|
|
82
|
+
const columns = Object.keys(attributes).map(name => {
|
|
83
|
+
const attribute = new this.Attribute(name, attributes[name]);
|
|
84
|
+
return attribute.toSqlString();
|
|
85
|
+
});
|
|
86
|
+
chunks.push(`(${columns.join(', ')})`);
|
|
87
|
+
await this.query(chunks.join(' '));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async alterTable(table, attributes) {
|
|
91
|
+
const { escapeId } = this;
|
|
92
|
+
const chunks = [ `ALTER TABLE ${escapeId(table)}` ];
|
|
93
|
+
|
|
94
|
+
const actions = Object.keys(attributes).map(name => {
|
|
95
|
+
const options = attributes[name];
|
|
96
|
+
// { [columnName]: { remove: true } }
|
|
97
|
+
if (options.remove) return `DROP COLUMN ${escapeId(name)}`;
|
|
98
|
+
const attribute = new this.Attribute(name, options);
|
|
99
|
+
return [
|
|
100
|
+
options.modify ? 'MODIFY COLUMN' : 'ADD COLUMN',
|
|
101
|
+
attribute.toSqlString(),
|
|
102
|
+
].join(' ');
|
|
103
|
+
});
|
|
104
|
+
chunks.push(actions.join(', '));
|
|
105
|
+
await this.query(chunks.join(' '));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async describeTable(table) {
|
|
109
|
+
const { database } = this.options;
|
|
110
|
+
const schemaInfo = await this.querySchemaInfo(database, table);
|
|
111
|
+
return schemaInfo[table].reduce(function(result, column) {
|
|
112
|
+
result[column.columnName] = column;
|
|
113
|
+
return result;
|
|
114
|
+
}, {});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async addColumn(table, name, params) {
|
|
118
|
+
const { escapeId } = this;
|
|
119
|
+
const attribute = new this.Attribute(name, params);
|
|
120
|
+
const sql = heresql(`
|
|
121
|
+
ALTER TABLE ${escapeId(table)}
|
|
122
|
+
ADD COLUMN ${attribute.toSqlString()}
|
|
123
|
+
`);
|
|
124
|
+
await this.query(sql);
|
|
17
125
|
}
|
|
126
|
+
|
|
127
|
+
async changeColumn(table, name, params) {
|
|
128
|
+
const { escapeId } = this;
|
|
129
|
+
const attribute = new this.Attribute(name, params);
|
|
130
|
+
const sql = heresql(`
|
|
131
|
+
ALTER TABLE ${escapeId(table)}
|
|
132
|
+
MODIFY COLUMN ${attribute.toSqlString()}
|
|
133
|
+
`);
|
|
134
|
+
await this.query(sql);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async removeColumn(table, name) {
|
|
138
|
+
const { escapeId } = this;
|
|
139
|
+
const { columnName } = new this.Attribute(name);
|
|
140
|
+
const sql = heresql(`
|
|
141
|
+
ALTER TABLE ${escapeId(table)} DROP COLUMN ${escapeId(columnName)}
|
|
142
|
+
`);
|
|
143
|
+
await this.query(sql);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async renameColumn(table, name, newName) {
|
|
147
|
+
const { escapeId } = this;
|
|
148
|
+
const { columnName } = new this.Attribute(name);
|
|
149
|
+
const attribute = new this.Attribute(newName);
|
|
150
|
+
const sql = heresql(`
|
|
151
|
+
ALTER TABLE ${escapeId(table)}
|
|
152
|
+
RENAME COLUMN ${escapeId(columnName)} TO ${escapeId(attribute.columnName)}
|
|
153
|
+
`);
|
|
154
|
+
await this.query(sql);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async renameTable(table, newTable) {
|
|
158
|
+
const { escapeId } = this;
|
|
159
|
+
const sql = heresql(`
|
|
160
|
+
ALTER TABLE ${escapeId(table)} RENAME TO ${escapeId(newTable)}
|
|
161
|
+
`);
|
|
162
|
+
await this.query(sql);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async dropTable(table) {
|
|
166
|
+
const { escapeId } = this;
|
|
167
|
+
await this.query(`DROP TABLE IF EXISTS ${escapeId(table)}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async truncateTable(table) {
|
|
171
|
+
const { escapeId } = this;
|
|
172
|
+
await this.query(`TRUNCATE TABLE ${escapeId(table)}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async addIndex(table, attributes, opts = {}) {
|
|
176
|
+
const { escapeId } = this;
|
|
177
|
+
const columns = attributes.map(name => new this.Attribute(name).columnName);
|
|
178
|
+
const type = opts.unique ? 'UNIQUE' : opts.type;
|
|
179
|
+
const prefix = type === 'UNIQUE' ? 'uk' : 'idx';
|
|
180
|
+
const { name } = {
|
|
181
|
+
name: [ prefix, table ].concat(columns).join('_'),
|
|
182
|
+
...opts,
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
if (type != null && ![ 'UNIQUE', 'FULLTEXT', 'SPATIAL' ].includes(type)) {
|
|
186
|
+
throw new Error(`Unexpected index type: ${type}`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const sql = heresql(`
|
|
190
|
+
CREATE ${type ? `${type} INDEX` : 'INDEX'} ${escapeId(name)}
|
|
191
|
+
ON ${escapeId(table)} (${columns.map(escapeId).join(', ')})
|
|
192
|
+
`);
|
|
193
|
+
await this.query(sql);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async removeIndex(table, attributes, opts = {}) {
|
|
197
|
+
const { escapeId } = this;
|
|
198
|
+
let name;
|
|
199
|
+
if (Array.isArray(attributes)) {
|
|
200
|
+
const columns = attributes.map(entry => new this.Attribute(entry).columnName);
|
|
201
|
+
const type = opts.unique ? 'UNIQUE' : opts.type;
|
|
202
|
+
const prefix = type === 'UNIQUE' ? 'uk' : 'idx';
|
|
203
|
+
name = [ prefix, table ].concat(columns).join('_');
|
|
204
|
+
} else if (typeof attributes === 'string') {
|
|
205
|
+
name = attributes;
|
|
206
|
+
} else {
|
|
207
|
+
throw new Error(`Unexpected index name: ${attributes}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const sql = this.type === 'mysql'
|
|
211
|
+
? `DROP INDEX ${escapeId(name)} ON ${escapeId(table)}`
|
|
212
|
+
: `DROP INDEX IF EXISTS ${escapeId(name)}`;
|
|
213
|
+
await this.query(sql);
|
|
214
|
+
}
|
|
215
|
+
|
|
18
216
|
};
|
|
217
|
+
|
|
218
|
+
module.exports = AbstractDriver;
|