@strapi/database 4.0.0-next.9 → 4.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/jest.config.js +10 -0
- package/lib/dialects/dialect.js +45 -0
- package/lib/dialects/index.js +7 -113
- package/lib/dialects/mysql/index.js +51 -0
- package/lib/dialects/mysql/schema-inspector.js +199 -0
- package/lib/dialects/postgresql/index.js +49 -0
- package/lib/dialects/postgresql/schema-inspector.js +232 -0
- package/lib/dialects/sqlite/index.js +73 -0
- package/lib/dialects/sqlite/schema-inspector.js +151 -0
- package/lib/entity-manager.js +18 -14
- package/lib/entity-repository.js +2 -3
- package/lib/errors.js +44 -2
- package/lib/fields.d.ts +2 -3
- package/lib/fields.js +7 -16
- package/lib/index.d.ts +53 -8
- package/lib/index.js +44 -27
- package/lib/lifecycles/index.d.ts +50 -0
- package/lib/{lifecycles.js → lifecycles/index.js} +25 -14
- package/lib/lifecycles/subscribers/index.d.ts +9 -0
- package/lib/lifecycles/subscribers/models-lifecycles.js +19 -0
- package/lib/lifecycles/subscribers/timestamps.js +65 -0
- package/lib/metadata/index.js +84 -95
- package/lib/metadata/relations.js +17 -1
- package/lib/migrations/index.d.ts +9 -0
- package/lib/migrations/index.js +69 -0
- package/lib/migrations/storage.js +51 -0
- package/lib/query/helpers/join.js +3 -5
- package/lib/query/helpers/order-by.js +21 -11
- package/lib/query/helpers/populate.js +35 -10
- package/lib/query/helpers/search.js +26 -12
- package/lib/query/helpers/transform.js +42 -14
- package/lib/query/helpers/where.js +92 -57
- package/lib/query/query-builder.js +116 -34
- package/lib/schema/__tests__/schema-diff.test.js +14 -1
- package/lib/schema/builder.js +315 -284
- package/lib/schema/diff.js +374 -0
- package/lib/schema/index.d.ts +49 -0
- package/lib/schema/index.js +47 -50
- package/lib/schema/schema.js +22 -27
- package/lib/schema/storage.js +79 -0
- package/lib/utils/content-types.js +0 -1
- package/package.json +27 -21
- package/examples/data.sqlite +0 -0
- package/lib/configuration.js +0 -49
- package/lib/schema/schema-diff.js +0 -337
- package/lib/schema/schema-storage.js +0 -44
|
@@ -5,12 +5,12 @@ const _ = require('lodash/fp');
|
|
|
5
5
|
const types = require('../../types');
|
|
6
6
|
const { createField } = require('../../fields');
|
|
7
7
|
|
|
8
|
-
const fromRow = (
|
|
8
|
+
const fromRow = (meta, row) => {
|
|
9
9
|
if (Array.isArray(row)) {
|
|
10
|
-
return row.map(singleRow => fromRow(
|
|
10
|
+
return row.map(singleRow => fromRow(meta, singleRow));
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
const { attributes } =
|
|
13
|
+
const { attributes } = meta;
|
|
14
14
|
|
|
15
15
|
if (_.isNil(row)) {
|
|
16
16
|
return null;
|
|
@@ -19,25 +19,16 @@ const fromRow = (metadata, row) => {
|
|
|
19
19
|
const obj = {};
|
|
20
20
|
|
|
21
21
|
for (const column in row) {
|
|
22
|
-
|
|
23
|
-
const attributeName = column;
|
|
24
|
-
|
|
25
|
-
if (!attributes[attributeName]) {
|
|
26
|
-
// ignore value that are not related to an attribute (join columns ...)
|
|
22
|
+
if (!_.has(column, meta.columnToAttribute)) {
|
|
27
23
|
continue;
|
|
28
24
|
}
|
|
29
25
|
|
|
26
|
+
const attributeName = meta.columnToAttribute[column];
|
|
30
27
|
const attribute = attributes[attributeName];
|
|
31
28
|
|
|
32
29
|
if (types.isScalar(attribute.type)) {
|
|
33
|
-
// TODO: we convert to column name
|
|
34
|
-
// TODO: handle default value too
|
|
35
|
-
// TODO: format data & use dialect to know which type they support (json particularly)
|
|
36
|
-
|
|
37
30
|
const field = createField(attribute);
|
|
38
31
|
|
|
39
|
-
// TODO: validate data on creation
|
|
40
|
-
// field.validate(data[attributeName]);
|
|
41
32
|
const val = row[column] === null ? null : field.fromDB(row[column]);
|
|
42
33
|
|
|
43
34
|
obj[attributeName] = val;
|
|
@@ -51,6 +42,43 @@ const fromRow = (metadata, row) => {
|
|
|
51
42
|
return obj;
|
|
52
43
|
};
|
|
53
44
|
|
|
45
|
+
const toRow = (meta, data = {}) => {
|
|
46
|
+
if (_.isNil(data)) {
|
|
47
|
+
return data;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (_.isArray(data)) {
|
|
51
|
+
return data.map(datum => toRow(meta, datum));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const { attributes } = meta;
|
|
55
|
+
|
|
56
|
+
for (const key in data) {
|
|
57
|
+
const attribute = attributes[key];
|
|
58
|
+
|
|
59
|
+
if (!attribute || attribute.columnName === key) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
data[attribute.columnName] = data[key];
|
|
64
|
+
delete data[key];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return data;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const toColumnName = (meta, name) => {
|
|
71
|
+
const attribute = meta.attributes[name];
|
|
72
|
+
|
|
73
|
+
if (!attribute) {
|
|
74
|
+
return name;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return attribute.columnName || name;
|
|
78
|
+
};
|
|
79
|
+
|
|
54
80
|
module.exports = {
|
|
81
|
+
toRow,
|
|
55
82
|
fromRow,
|
|
83
|
+
toColumnName,
|
|
56
84
|
};
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
const _ = require('lodash/fp');
|
|
4
4
|
|
|
5
5
|
const types = require('../../types');
|
|
6
|
+
const { createField } = require('../../fields');
|
|
6
7
|
const { createJoin } = require('./join');
|
|
8
|
+
const { toColumnName } = require('./transform');
|
|
7
9
|
|
|
8
10
|
const GROUP_OPERATORS = ['$and', '$or'];
|
|
9
11
|
const OPERATORS = [
|
|
@@ -19,8 +21,6 @@ const OPERATORS = [
|
|
|
19
21
|
'$null',
|
|
20
22
|
'$notNull',
|
|
21
23
|
'$between',
|
|
22
|
-
// '$like',
|
|
23
|
-
// '$regexp',
|
|
24
24
|
'$startsWith',
|
|
25
25
|
'$endsWith',
|
|
26
26
|
'$contains',
|
|
@@ -29,10 +29,65 @@ const OPERATORS = [
|
|
|
29
29
|
'$notContainsi',
|
|
30
30
|
];
|
|
31
31
|
|
|
32
|
+
const CAST_OPERATORS = [
|
|
33
|
+
'$not',
|
|
34
|
+
'$in',
|
|
35
|
+
'$notIn',
|
|
36
|
+
'$eq',
|
|
37
|
+
'$ne',
|
|
38
|
+
'$gt',
|
|
39
|
+
'$gte',
|
|
40
|
+
'$lt',
|
|
41
|
+
'$lte',
|
|
42
|
+
'$between',
|
|
43
|
+
];
|
|
44
|
+
|
|
32
45
|
const ARRAY_OPERATORS = ['$in', '$notIn', '$between'];
|
|
33
46
|
|
|
34
47
|
const isOperator = key => OPERATORS.includes(key);
|
|
35
48
|
|
|
49
|
+
const castValue = (value, attribute) => {
|
|
50
|
+
if (!attribute) {
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (types.isScalar(attribute.type)) {
|
|
55
|
+
const field = createField(attribute);
|
|
56
|
+
|
|
57
|
+
return value === null ? null : field.toDB(value);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return value;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const processAttributeWhere = (attribute, where, operator = '$eq') => {
|
|
64
|
+
if (_.isArray(where)) {
|
|
65
|
+
return where.map(sub => processAttributeWhere(attribute, sub, operator));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!_.isPlainObject(where)) {
|
|
69
|
+
if (CAST_OPERATORS.includes(operator)) {
|
|
70
|
+
return castValue(where, attribute);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return where;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const filters = {};
|
|
77
|
+
|
|
78
|
+
for (const key in where) {
|
|
79
|
+
const value = where[key];
|
|
80
|
+
|
|
81
|
+
if (!isOperator(key)) {
|
|
82
|
+
throw new Error(`Undefined attribute level operator ${key}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
filters[key] = processAttributeWhere(attribute, value, key);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return filters;
|
|
89
|
+
};
|
|
90
|
+
|
|
36
91
|
/**
|
|
37
92
|
* Process where parameter
|
|
38
93
|
* @param {Object} where
|
|
@@ -40,9 +95,13 @@ const isOperator = key => OPERATORS.includes(key);
|
|
|
40
95
|
* @param {number} depth
|
|
41
96
|
* @returns {Object}
|
|
42
97
|
*/
|
|
43
|
-
const processWhere = (where, ctx
|
|
44
|
-
if (
|
|
45
|
-
throw new Error('Where must be an object');
|
|
98
|
+
const processWhere = (where, ctx) => {
|
|
99
|
+
if (!_.isArray(where) && !_.isPlainObject(where)) {
|
|
100
|
+
throw new Error('Where must be an array or an object');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (_.isArray(where)) {
|
|
104
|
+
return where.map(sub => processWhere(sub, ctx));
|
|
46
105
|
}
|
|
47
106
|
|
|
48
107
|
const processNested = (where, ctx) => {
|
|
@@ -50,17 +109,17 @@ const processWhere = (where, ctx, depth = 0) => {
|
|
|
50
109
|
return where;
|
|
51
110
|
}
|
|
52
111
|
|
|
53
|
-
return processWhere(where, ctx
|
|
112
|
+
return processWhere(where, ctx);
|
|
54
113
|
};
|
|
55
114
|
|
|
56
|
-
const { db, uid, qb, alias
|
|
115
|
+
const { db, uid, qb, alias } = ctx;
|
|
116
|
+
const meta = db.metadata.get(uid);
|
|
57
117
|
|
|
58
118
|
const filters = {};
|
|
59
119
|
|
|
60
120
|
// for each key in where
|
|
61
121
|
for (const key in where) {
|
|
62
122
|
const value = where[key];
|
|
63
|
-
const attribute = db.metadata.get(uid).attributes[key];
|
|
64
123
|
|
|
65
124
|
// if operator $and $or then loop over them
|
|
66
125
|
if (GROUP_OPERATORS.includes(key)) {
|
|
@@ -74,36 +133,24 @@ const processWhere = (where, ctx, depth = 0) => {
|
|
|
74
133
|
}
|
|
75
134
|
|
|
76
135
|
if (isOperator(key)) {
|
|
77
|
-
|
|
78
|
-
throw new Error(
|
|
79
|
-
`Only $and, $or and $not can by used as root level operators. Found ${key}.`
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
filters[key] = processNested(value, ctx);
|
|
84
|
-
continue;
|
|
136
|
+
throw new Error(`Only $and, $or and $not can by used as root level operators. Found ${key}.`);
|
|
85
137
|
}
|
|
86
138
|
|
|
87
|
-
|
|
88
|
-
// TODO: if targeting a column name instead of an attribute
|
|
139
|
+
const attribute = meta.attributes[key];
|
|
89
140
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
filters[key] = processNested(value, ctx);
|
|
93
|
-
} else {
|
|
94
|
-
filters[`${alias || qb.alias}.${key}`] = processNested(value, ctx);
|
|
95
|
-
}
|
|
141
|
+
if (!attribute) {
|
|
142
|
+
filters[qb.aliasColumn(key, alias)] = processAttributeWhere(null, value);
|
|
96
143
|
continue;
|
|
97
|
-
|
|
98
|
-
// throw new Error(`Attribute ${key} not found on model ${uid}`);
|
|
99
144
|
}
|
|
100
145
|
|
|
101
|
-
|
|
102
|
-
if (attribute.type === 'relation') {
|
|
103
|
-
// TODO: pass down some filters (e.g published at)
|
|
104
|
-
|
|
146
|
+
if (types.isRelation(attribute.type)) {
|
|
105
147
|
// attribute
|
|
106
|
-
const subAlias = createJoin(ctx, {
|
|
148
|
+
const subAlias = createJoin(ctx, {
|
|
149
|
+
alias: alias || qb.alias,
|
|
150
|
+
uid,
|
|
151
|
+
attributeName: key,
|
|
152
|
+
attribute,
|
|
153
|
+
});
|
|
107
154
|
|
|
108
155
|
let nestedWhere = processNested(value, {
|
|
109
156
|
db,
|
|
@@ -113,7 +160,7 @@ const processWhere = (where, ctx, depth = 0) => {
|
|
|
113
160
|
});
|
|
114
161
|
|
|
115
162
|
if (!_.isPlainObject(nestedWhere) || isOperator(_.keys(nestedWhere)[0])) {
|
|
116
|
-
nestedWhere = { [
|
|
163
|
+
nestedWhere = { [qb.aliasColumn('id', subAlias)]: nestedWhere };
|
|
117
164
|
}
|
|
118
165
|
|
|
119
166
|
// TODO: use a better merge logic (push to $and when collisions)
|
|
@@ -123,9 +170,11 @@ const processWhere = (where, ctx, depth = 0) => {
|
|
|
123
170
|
}
|
|
124
171
|
|
|
125
172
|
if (types.isScalar(attribute.type)) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
173
|
+
const columnName = toColumnName(meta, key);
|
|
174
|
+
const aliasedColumnName = qb.aliasColumn(columnName, alias);
|
|
175
|
+
|
|
176
|
+
filters[aliasedColumnName] = processAttributeWhere(attribute, value);
|
|
177
|
+
|
|
129
178
|
continue;
|
|
130
179
|
}
|
|
131
180
|
|
|
@@ -135,6 +184,7 @@ const processWhere = (where, ctx, depth = 0) => {
|
|
|
135
184
|
return filters;
|
|
136
185
|
};
|
|
137
186
|
|
|
187
|
+
// TODO: add type casting per operator at some point
|
|
138
188
|
const applyOperator = (qb, column, operator, value) => {
|
|
139
189
|
if (Array.isArray(value) && !ARRAY_OPERATORS.includes(operator)) {
|
|
140
190
|
return qb.where(subQB => {
|
|
@@ -197,7 +247,6 @@ const applyOperator = (qb, column, operator, value) => {
|
|
|
197
247
|
break;
|
|
198
248
|
}
|
|
199
249
|
case '$null': {
|
|
200
|
-
// TODO: make this better
|
|
201
250
|
if (value) {
|
|
202
251
|
qb.whereNull(column);
|
|
203
252
|
}
|
|
@@ -207,26 +256,12 @@ const applyOperator = (qb, column, operator, value) => {
|
|
|
207
256
|
if (value) {
|
|
208
257
|
qb.whereNotNull(column);
|
|
209
258
|
}
|
|
210
|
-
|
|
211
259
|
break;
|
|
212
260
|
}
|
|
213
261
|
case '$between': {
|
|
214
262
|
qb.whereBetween(column, value);
|
|
215
263
|
break;
|
|
216
264
|
}
|
|
217
|
-
// case '$regexp': {
|
|
218
|
-
// // TODO:
|
|
219
|
-
//
|
|
220
|
-
// break;
|
|
221
|
-
// }
|
|
222
|
-
// // string
|
|
223
|
-
// // TODO: use $case to make it case insensitive
|
|
224
|
-
// case '$like': {
|
|
225
|
-
// qb.where(column, 'like', value);
|
|
226
|
-
// break;
|
|
227
|
-
// }
|
|
228
|
-
|
|
229
|
-
// TODO: add casting logic
|
|
230
265
|
case '$startsWith': {
|
|
231
266
|
qb.where(column, 'like', `${value}%`);
|
|
232
267
|
break;
|
|
@@ -260,7 +295,7 @@ const applyOperator = (qb, column, operator, value) => {
|
|
|
260
295
|
// TODO: relational operators every/some/exists/size ...
|
|
261
296
|
|
|
262
297
|
default: {
|
|
263
|
-
throw new Error(`Undefined operator ${operator}`);
|
|
298
|
+
throw new Error(`Undefined attribute level operator ${operator}`);
|
|
264
299
|
}
|
|
265
300
|
}
|
|
266
301
|
};
|
|
@@ -274,7 +309,6 @@ const applyWhereToColumn = (qb, column, columnWhere) => {
|
|
|
274
309
|
return qb.where(column, columnWhere);
|
|
275
310
|
}
|
|
276
311
|
|
|
277
|
-
// TODO: handle casing
|
|
278
312
|
Object.keys(columnWhere).forEach(operator => {
|
|
279
313
|
const value = columnWhere[operator];
|
|
280
314
|
|
|
@@ -283,12 +317,12 @@ const applyWhereToColumn = (qb, column, columnWhere) => {
|
|
|
283
317
|
};
|
|
284
318
|
|
|
285
319
|
const applyWhere = (qb, where) => {
|
|
286
|
-
if (
|
|
287
|
-
|
|
320
|
+
if (!_.isArray(where) && !_.isPlainObject(where)) {
|
|
321
|
+
throw new Error('Where must be an array or an object');
|
|
288
322
|
}
|
|
289
323
|
|
|
290
|
-
if (
|
|
291
|
-
|
|
324
|
+
if (_.isArray(where)) {
|
|
325
|
+
return qb.where(subQB => where.forEach(subWhere => applyWhere(subQB, subWhere)));
|
|
292
326
|
}
|
|
293
327
|
|
|
294
328
|
Object.keys(where).forEach(key => {
|
|
@@ -316,9 +350,10 @@ const applyWhere = (qb, where) => {
|
|
|
316
350
|
|
|
317
351
|
const fieldLowerFn = qb => {
|
|
318
352
|
// Postgres requires string to be passed
|
|
319
|
-
if (qb.client.config.client === '
|
|
353
|
+
if (qb.client.config.client === 'postgres') {
|
|
320
354
|
return 'LOWER(CAST(?? AS VARCHAR))';
|
|
321
355
|
}
|
|
356
|
+
|
|
322
357
|
return 'LOWER(??)';
|
|
323
358
|
};
|
|
324
359
|
|
|
@@ -29,22 +29,17 @@ const createQueryBuilder = (uid, db) => {
|
|
|
29
29
|
return {
|
|
30
30
|
alias: getAlias(),
|
|
31
31
|
getAlias,
|
|
32
|
+
state,
|
|
32
33
|
|
|
33
34
|
select(args) {
|
|
34
35
|
state.type = 'select';
|
|
35
|
-
state.select = _.uniq(_.castArray(args))
|
|
36
|
+
state.select = _.uniq(_.castArray(args));
|
|
36
37
|
|
|
37
38
|
return this;
|
|
38
39
|
},
|
|
39
40
|
|
|
40
41
|
addSelect(args) {
|
|
41
|
-
_.uniq(_.castArray(args))
|
|
42
|
-
.map(col => this.aliasColumn(col))
|
|
43
|
-
.forEach(toSelect => {
|
|
44
|
-
if (!state.select.includes(toSelect)) {
|
|
45
|
-
state.select.push(toSelect);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
42
|
+
state.select = _.uniq([...state.select, ..._.castArray(args)]);
|
|
48
43
|
|
|
49
44
|
return this;
|
|
50
45
|
},
|
|
@@ -62,6 +57,10 @@ const createQueryBuilder = (uid, db) => {
|
|
|
62
57
|
return this;
|
|
63
58
|
},
|
|
64
59
|
|
|
60
|
+
ref(name) {
|
|
61
|
+
return db.connection.ref(helpers.toColumnName(meta, name));
|
|
62
|
+
},
|
|
63
|
+
|
|
65
64
|
update(data) {
|
|
66
65
|
state.type = 'update';
|
|
67
66
|
state.data = data;
|
|
@@ -77,9 +76,11 @@ const createQueryBuilder = (uid, db) => {
|
|
|
77
76
|
},
|
|
78
77
|
|
|
79
78
|
where(where = {}) {
|
|
80
|
-
|
|
79
|
+
if (!_.isPlainObject(where)) {
|
|
80
|
+
throw new Error('Where must be an object');
|
|
81
|
+
}
|
|
81
82
|
|
|
82
|
-
state.where.push(
|
|
83
|
+
state.where.push(where);
|
|
83
84
|
|
|
84
85
|
return this;
|
|
85
86
|
},
|
|
@@ -95,7 +96,7 @@ const createQueryBuilder = (uid, db) => {
|
|
|
95
96
|
},
|
|
96
97
|
|
|
97
98
|
orderBy(orderBy) {
|
|
98
|
-
state.orderBy =
|
|
99
|
+
state.orderBy = orderBy;
|
|
99
100
|
return this;
|
|
100
101
|
},
|
|
101
102
|
|
|
@@ -105,7 +106,7 @@ const createQueryBuilder = (uid, db) => {
|
|
|
105
106
|
},
|
|
106
107
|
|
|
107
108
|
populate(populate) {
|
|
108
|
-
state.populate =
|
|
109
|
+
state.populate = populate;
|
|
109
110
|
return this;
|
|
110
111
|
},
|
|
111
112
|
|
|
@@ -115,7 +116,7 @@ const createQueryBuilder = (uid, db) => {
|
|
|
115
116
|
},
|
|
116
117
|
|
|
117
118
|
init(params = {}) {
|
|
118
|
-
const { _q, where, select, limit, offset, orderBy, groupBy, populate } = params;
|
|
119
|
+
const { _q, filters, where, select, limit, offset, orderBy, groupBy, populate } = params;
|
|
119
120
|
|
|
120
121
|
if (!_.isNil(where)) {
|
|
121
122
|
this.where(where);
|
|
@@ -151,9 +152,17 @@ const createQueryBuilder = (uid, db) => {
|
|
|
151
152
|
this.populate(populate);
|
|
152
153
|
}
|
|
153
154
|
|
|
155
|
+
if (!_.isNil(filters)) {
|
|
156
|
+
this.filters(filters);
|
|
157
|
+
}
|
|
158
|
+
|
|
154
159
|
return this;
|
|
155
160
|
},
|
|
156
161
|
|
|
162
|
+
filters(filters) {
|
|
163
|
+
state.filters = filters;
|
|
164
|
+
},
|
|
165
|
+
|
|
157
166
|
first() {
|
|
158
167
|
state.first = true;
|
|
159
168
|
return this;
|
|
@@ -164,43 +173,111 @@ const createQueryBuilder = (uid, db) => {
|
|
|
164
173
|
return this;
|
|
165
174
|
},
|
|
166
175
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
176
|
+
mustUseAlias() {
|
|
177
|
+
return ['select', 'count'].includes(state.type);
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
aliasColumn(key, alias) {
|
|
181
|
+
if (typeof key !== 'string') {
|
|
182
|
+
return key;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (key.indexOf('.') >= 0) {
|
|
186
|
+
return key;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!_.isNil(alias)) {
|
|
190
|
+
return `${alias}.${key}`;
|
|
170
191
|
}
|
|
171
192
|
|
|
172
|
-
|
|
173
|
-
return this.alias + '.' + columnName;
|
|
193
|
+
return this.mustUseAlias() ? `${this.alias}.${key}` : key;
|
|
174
194
|
},
|
|
175
195
|
|
|
176
196
|
raw(...args) {
|
|
177
197
|
return db.connection.raw(...args);
|
|
178
198
|
},
|
|
179
199
|
|
|
180
|
-
|
|
181
|
-
|
|
200
|
+
shouldUseSubQuery() {
|
|
201
|
+
return ['delete', 'update'].includes(state.type) && state.joins.length > 0;
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
runSubQuery() {
|
|
205
|
+
this.select('id');
|
|
206
|
+
const subQB = this.getKnexQuery();
|
|
182
207
|
|
|
183
|
-
const
|
|
208
|
+
const nestedSubQuery = db
|
|
209
|
+
.getConnection()
|
|
210
|
+
.select('id')
|
|
211
|
+
.from(subQB.as('subQuery'));
|
|
184
212
|
|
|
213
|
+
return db
|
|
214
|
+
.getConnection(tableName)
|
|
215
|
+
[state.type]()
|
|
216
|
+
.whereIn('id', nestedSubQuery);
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
processState() {
|
|
220
|
+
state.orderBy = helpers.processOrderBy(state.orderBy, { qb: this, uid, db });
|
|
221
|
+
|
|
222
|
+
if (!_.isNil(state.filters)) {
|
|
223
|
+
if (_.isFunction(state.filters)) {
|
|
224
|
+
const filters = state.filters({ qb: this, uid, meta, db });
|
|
225
|
+
|
|
226
|
+
if (!_.isNil(filters)) {
|
|
227
|
+
state.where.push(filters);
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
state.where.push(state.filters);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
state.where = helpers.processWhere(state.where, { qb: this, uid, db });
|
|
235
|
+
state.populate = helpers.processPopulate(state.populate, { qb: this, uid, db });
|
|
236
|
+
state.data = helpers.toRow(meta, state.data);
|
|
237
|
+
|
|
238
|
+
this.processSelect();
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
shouldUseDistinct() {
|
|
242
|
+
return state.joins.length > 0 && _.isEmpty(state.groupBy);
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
processSelect() {
|
|
246
|
+
state.select = state.select.map(field => helpers.toColumnName(meta, field));
|
|
247
|
+
|
|
248
|
+
if (this.shouldUseDistinct()) {
|
|
249
|
+
const joinsOrderByColumns = state.joins.flatMap(join => {
|
|
250
|
+
return _.keys(join.orderBy).map(key => this.aliasColumn(key, join.alias));
|
|
251
|
+
});
|
|
252
|
+
const orderByColumns = state.orderBy.map(({ column }) => column);
|
|
253
|
+
|
|
254
|
+
state.select = _.uniq([...joinsOrderByColumns, ...orderByColumns, ...state.select]);
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
getKnexQuery() {
|
|
185
259
|
if (!state.type) {
|
|
186
260
|
this.select('*');
|
|
187
261
|
}
|
|
188
262
|
|
|
263
|
+
const aliasedTableName = this.mustUseAlias() ? `${tableName} as ${this.alias}` : tableName;
|
|
264
|
+
|
|
265
|
+
const qb = db.getConnection(aliasedTableName);
|
|
266
|
+
|
|
267
|
+
if (this.shouldUseSubQuery()) {
|
|
268
|
+
return this.runSubQuery();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
this.processState();
|
|
272
|
+
|
|
189
273
|
switch (state.type) {
|
|
190
274
|
case 'select': {
|
|
191
|
-
|
|
192
|
-
state.select = [this.aliasColumn('*')];
|
|
193
|
-
}
|
|
275
|
+
qb.select(state.select.map(column => this.aliasColumn(column)));
|
|
194
276
|
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
// TODO: make sure we return the right data
|
|
198
|
-
qb.distinct(`${this.alias}.id`);
|
|
199
|
-
// TODO: add column if they aren't there already
|
|
200
|
-
state.select.unshift(...state.orderBy.map(({ column }) => column));
|
|
277
|
+
if (this.shouldUseDistinct()) {
|
|
278
|
+
qb.distinct();
|
|
201
279
|
}
|
|
202
280
|
|
|
203
|
-
qb.select(state.select);
|
|
204
281
|
break;
|
|
205
282
|
}
|
|
206
283
|
case 'count': {
|
|
@@ -218,14 +295,17 @@ const createQueryBuilder = (uid, db) => {
|
|
|
218
295
|
}
|
|
219
296
|
case 'update': {
|
|
220
297
|
qb.update(state.data);
|
|
221
|
-
|
|
222
298
|
break;
|
|
223
299
|
}
|
|
224
300
|
case 'delete': {
|
|
225
|
-
qb.
|
|
301
|
+
qb.delete();
|
|
226
302
|
|
|
227
303
|
break;
|
|
228
304
|
}
|
|
305
|
+
case 'truncate': {
|
|
306
|
+
db.truncate();
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
229
309
|
}
|
|
230
310
|
|
|
231
311
|
if (state.limit) {
|
|
@@ -248,13 +328,15 @@ const createQueryBuilder = (uid, db) => {
|
|
|
248
328
|
qb.groupBy(state.groupBy);
|
|
249
329
|
}
|
|
250
330
|
|
|
331
|
+
// if there are joins and it is a delete or update use a sub query
|
|
251
332
|
if (state.where) {
|
|
252
333
|
helpers.applyWhere(qb, state.where);
|
|
253
334
|
}
|
|
254
335
|
|
|
336
|
+
// if there are joins and it is a delete or update use a sub query
|
|
255
337
|
if (state.search) {
|
|
256
338
|
qb.where(subQb => {
|
|
257
|
-
helpers.applySearch(subQb, state.search, {
|
|
339
|
+
helpers.applySearch(subQb, state.search, { qb: this, db, uid });
|
|
258
340
|
});
|
|
259
341
|
}
|
|
260
342
|
|
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const createSchemaDiff = require('../diff');
|
|
4
4
|
|
|
5
|
+
let diffSchemas;
|
|
5
6
|
describe('diffSchemas', () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
const schemaDiff = createSchemaDiff({
|
|
9
|
+
dialect: {
|
|
10
|
+
usesForeignKeys() {
|
|
11
|
+
return true;
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
diffSchemas = schemaDiff.diff.bind(schemaDiff);
|
|
17
|
+
});
|
|
18
|
+
|
|
6
19
|
test('New Table', () => {
|
|
7
20
|
const testTable = {
|
|
8
21
|
name: 'my_table',
|