@mikro-orm/knex 6.0.0-dev.11 → 6.0.0-dev.110
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/AbstractSqlConnection.d.ts +8 -4
- package/AbstractSqlConnection.js +30 -18
- package/AbstractSqlDriver.d.ts +16 -17
- package/AbstractSqlDriver.js +271 -113
- package/AbstractSqlPlatform.d.ts +5 -4
- package/AbstractSqlPlatform.js +34 -5
- package/MonkeyPatchable.d.ts +1 -0
- package/MonkeyPatchable.js +3 -0
- package/README.md +64 -107
- package/SqlEntityManager.d.ts +2 -6
- package/SqlEntityManager.js +5 -9
- package/SqlEntityRepository.d.ts +7 -6
- package/SqlEntityRepository.js +11 -8
- package/index.mjs +22 -6
- package/package.json +8 -8
- package/query/ArrayCriteriaNode.d.ts +3 -3
- package/query/CriteriaNode.d.ts +8 -8
- package/query/CriteriaNode.js +16 -9
- package/query/CriteriaNodeFactory.d.ts +6 -6
- package/query/CriteriaNodeFactory.js +10 -13
- package/query/ObjectCriteriaNode.d.ts +3 -3
- package/query/ObjectCriteriaNode.js +12 -8
- package/query/QueryBuilder.d.ts +39 -19
- package/query/QueryBuilder.js +331 -111
- package/query/QueryBuilderHelper.d.ts +9 -13
- package/query/QueryBuilderHelper.js +153 -107
- package/query/ScalarCriteriaNode.d.ts +3 -2
- package/query/ScalarCriteriaNode.js +9 -6
- package/query/enums.js +1 -1
- package/schema/DatabaseSchema.d.ts +5 -2
- package/schema/DatabaseSchema.js +26 -8
- package/schema/DatabaseTable.d.ts +4 -2
- package/schema/DatabaseTable.js +54 -29
- package/schema/SchemaComparator.js +12 -2
- package/schema/SchemaHelper.d.ts +6 -4
- package/schema/SchemaHelper.js +11 -4
- package/schema/SqlSchemaGenerator.d.ts +3 -6
- package/schema/SqlSchemaGenerator.js +61 -28
- package/typings.d.ts +13 -7
package/AbstractSqlDriver.js
CHANGED
|
@@ -5,9 +5,12 @@ const core_1 = require("@mikro-orm/core");
|
|
|
5
5
|
const query_1 = require("./query");
|
|
6
6
|
const SqlEntityManager_1 = require("./SqlEntityManager");
|
|
7
7
|
class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
8
|
+
[core_1.EntityManagerType];
|
|
9
|
+
connection;
|
|
10
|
+
replicas = [];
|
|
11
|
+
platform;
|
|
8
12
|
constructor(config, platform, connection, connector) {
|
|
9
13
|
super(config, connector);
|
|
10
|
-
this.replicas = [];
|
|
11
14
|
this.connection = new connection(this.config);
|
|
12
15
|
this.replicas = this.createReplicas(conf => new connection(this.config, conf, 'read'));
|
|
13
16
|
this.platform = platform;
|
|
@@ -26,19 +29,31 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
26
29
|
}
|
|
27
30
|
const populate = this.autoJoinOneToOneOwner(meta, options.populate, options.fields);
|
|
28
31
|
const joinedProps = this.joinedProps(meta, populate);
|
|
29
|
-
const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false);
|
|
32
|
+
const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging);
|
|
30
33
|
const fields = this.buildFields(meta, populate, joinedProps, qb, options.fields);
|
|
31
34
|
const joinedPropsOrderBy = this.buildJoinedPropsOrderBy(entityName, qb, meta, joinedProps);
|
|
35
|
+
const orderBy = [...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy];
|
|
32
36
|
if (core_1.Utils.isPrimaryKey(where, meta.compositePK)) {
|
|
33
37
|
where = { [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
|
|
34
38
|
}
|
|
39
|
+
const { first, last, before, after } = options;
|
|
40
|
+
const isCursorPagination = [first, last, before, after].some(v => v != null);
|
|
35
41
|
qb.select(fields)
|
|
36
42
|
.populate(populate, joinedProps.length > 0 ? options.populateWhere : undefined)
|
|
37
43
|
.where(where)
|
|
38
|
-
.orderBy([...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy])
|
|
39
44
|
.groupBy(options.groupBy)
|
|
40
45
|
.having(options.having)
|
|
46
|
+
.indexHint(options.indexHint)
|
|
47
|
+
.comment(options.comments)
|
|
48
|
+
.hintComment(options.hintComments)
|
|
41
49
|
.withSchema(this.getSchemaName(meta, options));
|
|
50
|
+
if (isCursorPagination) {
|
|
51
|
+
const { orderBy: newOrderBy, where } = this.processCursorOptions(meta, options, orderBy);
|
|
52
|
+
qb.andWhere(where).orderBy(newOrderBy);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
qb.orderBy(orderBy);
|
|
56
|
+
}
|
|
42
57
|
if (options.limit !== undefined) {
|
|
43
58
|
qb.limit(options.limit, options.offset);
|
|
44
59
|
}
|
|
@@ -47,8 +62,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
47
62
|
}
|
|
48
63
|
core_1.Utils.asArray(options.flags).forEach(flag => qb.setFlag(flag));
|
|
49
64
|
const result = await this.rethrow(qb.execute('all'));
|
|
65
|
+
if (isCursorPagination && !first && !!last) {
|
|
66
|
+
result.reverse();
|
|
67
|
+
}
|
|
50
68
|
if (joinedProps.length > 0) {
|
|
51
|
-
return this.mergeJoinedResult(result, meta);
|
|
69
|
+
return this.mergeJoinedResult(result, meta, joinedProps);
|
|
52
70
|
}
|
|
53
71
|
return result;
|
|
54
72
|
}
|
|
@@ -61,58 +79,57 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
61
79
|
opts.limit = 1;
|
|
62
80
|
}
|
|
63
81
|
if (opts.limit > 0 && !opts.flags?.includes(core_1.QueryFlag.DISABLE_PAGINATE)) {
|
|
64
|
-
opts.flags
|
|
82
|
+
opts.flags ??= [];
|
|
65
83
|
opts.flags.push(core_1.QueryFlag.DISABLE_PAGINATE);
|
|
66
84
|
}
|
|
67
85
|
const res = await this.find(entityName, where, opts);
|
|
68
86
|
return res[0] || null;
|
|
69
87
|
}
|
|
70
88
|
async findVirtual(entityName, where, options) {
|
|
71
|
-
|
|
72
|
-
/* istanbul ignore next */
|
|
73
|
-
if (!meta.expression) {
|
|
74
|
-
return [];
|
|
75
|
-
}
|
|
76
|
-
if (typeof meta.expression === 'string') {
|
|
77
|
-
return this.wrapVirtualExpressionInSubquery(meta, meta.expression, where, options);
|
|
78
|
-
}
|
|
79
|
-
const em = this.createEntityManager(false);
|
|
80
|
-
em.setTransactionContext(options.ctx);
|
|
81
|
-
const res = meta.expression(em, where, options);
|
|
82
|
-
if (res instanceof (query_1.QueryBuilder)) {
|
|
83
|
-
return this.wrapVirtualExpressionInSubquery(meta, res.getFormattedQuery(), where, options);
|
|
84
|
-
}
|
|
85
|
-
/* istanbul ignore next */
|
|
86
|
-
return res;
|
|
89
|
+
return this.findFromVirtual(entityName, where, options, query_1.QueryType.SELECT);
|
|
87
90
|
}
|
|
88
91
|
async countVirtual(entityName, where, options) {
|
|
92
|
+
return this.findFromVirtual(entityName, where, options, query_1.QueryType.COUNT);
|
|
93
|
+
}
|
|
94
|
+
async findFromVirtual(entityName, where, options, type) {
|
|
89
95
|
const meta = this.metadata.get(entityName);
|
|
90
96
|
/* istanbul ignore next */
|
|
91
97
|
if (!meta.expression) {
|
|
92
|
-
return 0;
|
|
98
|
+
return type === query_1.QueryType.SELECT ? [] : 0;
|
|
93
99
|
}
|
|
94
100
|
if (typeof meta.expression === 'string') {
|
|
95
|
-
return this.wrapVirtualExpressionInSubquery(meta, meta.expression, where, options,
|
|
101
|
+
return this.wrapVirtualExpressionInSubquery(meta, meta.expression, where, options, type);
|
|
96
102
|
}
|
|
97
103
|
const em = this.createEntityManager(false);
|
|
98
104
|
em.setTransactionContext(options.ctx);
|
|
99
105
|
const res = meta.expression(em, where, options);
|
|
100
|
-
if (res
|
|
101
|
-
return this.wrapVirtualExpressionInSubquery(meta, res
|
|
106
|
+
if (typeof res === 'string') {
|
|
107
|
+
return this.wrapVirtualExpressionInSubquery(meta, res, where, options, type);
|
|
108
|
+
}
|
|
109
|
+
if (res instanceof query_1.QueryBuilder) {
|
|
110
|
+
return this.wrapVirtualExpressionInSubquery(meta, res.getFormattedQuery(), where, options, type);
|
|
111
|
+
}
|
|
112
|
+
if (core_1.Utils.isObject(res)) {
|
|
113
|
+
const { sql, bindings } = res.toSQL();
|
|
114
|
+
const query = this.platform.formatQuery(sql, bindings);
|
|
115
|
+
return this.wrapVirtualExpressionInSubquery(meta, query, where, options, type);
|
|
102
116
|
}
|
|
103
117
|
/* istanbul ignore next */
|
|
104
118
|
return res;
|
|
105
119
|
}
|
|
106
|
-
async wrapVirtualExpressionInSubquery(meta, expression, where, options, type
|
|
120
|
+
async wrapVirtualExpressionInSubquery(meta, expression, where, options, type) {
|
|
107
121
|
const qb = this.createQueryBuilder(meta.className, options?.ctx, options.connectionType, options.convertCustomTypes)
|
|
108
|
-
.limit(options?.limit, options?.offset)
|
|
122
|
+
.limit(options?.limit, options?.offset)
|
|
123
|
+
.indexHint(options.indexHint)
|
|
124
|
+
.comment(options.comments)
|
|
125
|
+
.hintComment(options.hintComments);
|
|
109
126
|
if (options.orderBy) {
|
|
110
127
|
qb.orderBy(options.orderBy);
|
|
111
128
|
}
|
|
112
129
|
qb.where(where);
|
|
113
130
|
const kqb = qb.getKnexQuery().clear('select');
|
|
114
131
|
if (type === query_1.QueryType.COUNT) {
|
|
115
|
-
kqb.select(
|
|
132
|
+
kqb.select(this.connection.getKnex().raw('count(*) as count'));
|
|
116
133
|
}
|
|
117
134
|
else { // select
|
|
118
135
|
kqb.select('*');
|
|
@@ -131,7 +148,10 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
131
148
|
return null;
|
|
132
149
|
}
|
|
133
150
|
if (qb) {
|
|
151
|
+
// here we map the aliased results (cartesian product) to an object graph
|
|
134
152
|
this.mapJoinedProps(ret, meta, populate, qb, ret, map);
|
|
153
|
+
// we need to remove the cycles from the mapped values
|
|
154
|
+
core_1.Utils.removeCycles(ret, meta);
|
|
135
155
|
}
|
|
136
156
|
return ret;
|
|
137
157
|
}
|
|
@@ -146,19 +166,33 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
146
166
|
const meta2 = this.metadata.find(relation.type);
|
|
147
167
|
const path = parentJoinPath ? `${parentJoinPath}.${relation.name}` : `${meta.name}.${relation.name}`;
|
|
148
168
|
const relationAlias = qb.getAliasForJoinPath(path);
|
|
149
|
-
|
|
169
|
+
let relationPojo = {};
|
|
150
170
|
// If the primary key value for the relation is null, we know we haven't joined to anything
|
|
151
171
|
// and therefore we don't return any record (since all values would be null)
|
|
152
172
|
const hasPK = meta2.primaryKeys.every(pk => meta2.properties[pk].fieldNames.every(name => {
|
|
153
173
|
return root[`${relationAlias}__${name}`] != null;
|
|
154
174
|
}));
|
|
155
175
|
if (!hasPK) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
176
|
+
if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
|
|
177
|
+
result[relation.name] ??= [];
|
|
178
|
+
}
|
|
179
|
+
if ([core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(relation.kind)) {
|
|
180
|
+
result[relation.name] = null;
|
|
159
181
|
}
|
|
160
182
|
return;
|
|
161
183
|
}
|
|
184
|
+
const tz = this.platform.getTimezone();
|
|
185
|
+
meta2.props
|
|
186
|
+
.filter(prop => prop.persist === false && prop.fieldNames)
|
|
187
|
+
.forEach(prop => {
|
|
188
|
+
if (prop.fieldNames.length > 1) { // composite keys
|
|
189
|
+
relationPojo[prop.name] = prop.fieldNames.map(name => root[`${relationAlias}__${name}`]);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
const alias = `${relationAlias}__${prop.fieldNames[0]}`;
|
|
193
|
+
relationPojo[prop.name] = root[alias];
|
|
194
|
+
}
|
|
195
|
+
});
|
|
162
196
|
meta2.props
|
|
163
197
|
.filter(prop => this.platform.shouldHaveColumn(prop, p.children || []))
|
|
164
198
|
.forEach(prop => {
|
|
@@ -166,21 +200,37 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
166
200
|
relationPojo[prop.name] = prop.fieldNames.map(name => root[`${relationAlias}__${name}`]);
|
|
167
201
|
prop.fieldNames.map(name => delete root[`${relationAlias}__${name}`]);
|
|
168
202
|
}
|
|
203
|
+
else if (prop.runtimeType === 'Date') {
|
|
204
|
+
const alias = `${relationAlias}__${prop.fieldNames[0]}`;
|
|
205
|
+
relationPojo[prop.name] = (typeof root[alias] === 'string' ? new Date(root[alias]) : root[alias]);
|
|
206
|
+
delete root[alias];
|
|
207
|
+
}
|
|
169
208
|
else {
|
|
170
209
|
const alias = `${relationAlias}__${prop.fieldNames[0]}`;
|
|
171
210
|
relationPojo[prop.name] = root[alias];
|
|
172
211
|
delete root[alias];
|
|
173
212
|
}
|
|
174
213
|
});
|
|
175
|
-
const key = `${meta.name}-${
|
|
214
|
+
const key = `${meta.name}-${core_1.Utils.getCompositeKeyHash(result, meta)}`;
|
|
215
|
+
const key2 = `${meta2.name}-${core_1.Utils.getCompositeKeyHash(relationPojo, meta2)}`;
|
|
216
|
+
if (map[key2]) {
|
|
217
|
+
const old = map[key2];
|
|
218
|
+
core_1.Utils.keys(relationPojo)
|
|
219
|
+
.filter(k => relationPojo[k] != null)
|
|
220
|
+
.forEach(k => old[k] = relationPojo[k]);
|
|
221
|
+
relationPojo = old;
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
map[key2] = relationPojo;
|
|
225
|
+
}
|
|
176
226
|
if (map[key]) {
|
|
177
227
|
result[relation.name] = map[key][relation.name];
|
|
178
228
|
}
|
|
179
229
|
else {
|
|
180
230
|
map[key] = result;
|
|
181
231
|
}
|
|
182
|
-
if ([core_1.
|
|
183
|
-
result[relation.name]
|
|
232
|
+
if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
|
|
233
|
+
result[relation.name] ??= [];
|
|
184
234
|
this.appendToCollection(meta2, result[relation.name], relationPojo);
|
|
185
235
|
}
|
|
186
236
|
else {
|
|
@@ -207,6 +257,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
207
257
|
return this.countVirtual(entityName, where, options);
|
|
208
258
|
}
|
|
209
259
|
const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false)
|
|
260
|
+
.indexHint(options.indexHint)
|
|
261
|
+
.comment(options.comments)
|
|
262
|
+
.hintComment(options.hintComments)
|
|
210
263
|
.groupBy(options.groupBy)
|
|
211
264
|
.having(options.having)
|
|
212
265
|
.populate(options.populate ?? [])
|
|
@@ -215,7 +268,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
215
268
|
return this.rethrow(qb.getCount());
|
|
216
269
|
}
|
|
217
270
|
async nativeInsert(entityName, data, options = {}) {
|
|
218
|
-
options.convertCustomTypes
|
|
271
|
+
options.convertCustomTypes ??= true;
|
|
219
272
|
const meta = this.metadata.find(entityName);
|
|
220
273
|
const collections = this.extractManyToMany(entityName, data);
|
|
221
274
|
const pks = meta?.primaryKeys ?? [this.config.getNamingStrategy().referenceColumnName()];
|
|
@@ -235,16 +288,20 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
235
288
|
return res;
|
|
236
289
|
}
|
|
237
290
|
async nativeInsertMany(entityName, data, options = {}) {
|
|
238
|
-
options.processCollections
|
|
239
|
-
options.convertCustomTypes
|
|
291
|
+
options.processCollections ??= true;
|
|
292
|
+
options.convertCustomTypes ??= true;
|
|
240
293
|
const meta = this.metadata.find(entityName);
|
|
241
294
|
const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
|
|
242
295
|
const pks = this.getPrimaryKeyFields(entityName);
|
|
243
296
|
const set = new Set();
|
|
244
|
-
data.forEach(row =>
|
|
297
|
+
data.forEach(row => core_1.Utils.keys(row).forEach(k => set.add(k)));
|
|
245
298
|
const props = [...set].map(name => meta?.properties[name] ?? { name, fieldNames: [name] });
|
|
246
|
-
|
|
299
|
+
let fields = core_1.Utils.flatten(props.map(prop => prop.fieldNames));
|
|
300
|
+
const duplicates = core_1.Utils.findDuplicates(fields);
|
|
247
301
|
const params = [];
|
|
302
|
+
if (duplicates.length) {
|
|
303
|
+
fields = core_1.Utils.unique(fields);
|
|
304
|
+
}
|
|
248
305
|
/* istanbul ignore next */
|
|
249
306
|
const tableName = meta ? this.getTableName(meta, options) : this.platform.quoteIdentifier(entityName);
|
|
250
307
|
let sql = `insert into ${tableName} `;
|
|
@@ -255,29 +312,43 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
255
312
|
else {
|
|
256
313
|
sql += ' ' + data.map(() => `select null as ${this.platform.quoteIdentifier(pks[0])}`).join(' union all ');
|
|
257
314
|
}
|
|
315
|
+
const addParams = (prop, row) => {
|
|
316
|
+
if (options.convertCustomTypes && prop.customType) {
|
|
317
|
+
params.push(prop.customType.convertToDatabaseValue(row[prop.name], this.platform, { key: prop.name, mode: 'query-data' }));
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
params.push(row[prop.name]);
|
|
321
|
+
};
|
|
258
322
|
if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
|
|
259
323
|
sql += data.map(row => {
|
|
260
324
|
const keys = [];
|
|
261
325
|
props.forEach(prop => {
|
|
262
326
|
if (prop.fieldNames.length > 1) {
|
|
263
|
-
|
|
264
|
-
|
|
327
|
+
const param = core_1.Utils.flatten([...row[prop.name] ?? prop.fieldNames.map(() => null)]);
|
|
328
|
+
const key = param.map(() => '?');
|
|
329
|
+
prop.fieldNames.forEach((field, idx) => {
|
|
330
|
+
if (!duplicates.includes(field)) {
|
|
331
|
+
params.push(param[idx]);
|
|
332
|
+
keys.push(key[idx]);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
265
335
|
}
|
|
266
336
|
else if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(row[prop.name])) {
|
|
267
337
|
keys.push(prop.customType.convertToDatabaseValueSQL('?', this.platform));
|
|
268
|
-
|
|
338
|
+
addParams(prop, row);
|
|
269
339
|
}
|
|
270
340
|
else {
|
|
271
|
-
|
|
341
|
+
addParams(prop, row);
|
|
272
342
|
keys.push('?');
|
|
273
343
|
}
|
|
274
344
|
});
|
|
275
345
|
return '(' + (keys.join(', ') || 'default') + ')';
|
|
276
346
|
}).join(', ');
|
|
277
347
|
}
|
|
278
|
-
if (this.platform.usesReturningStatement()) {
|
|
279
|
-
|
|
280
|
-
|
|
348
|
+
if (meta && this.platform.usesReturningStatement()) {
|
|
349
|
+
const returningProps = meta.props
|
|
350
|
+
.filter(prop => prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw))
|
|
351
|
+
.filter(prop => !(prop.name in data[0]) || core_1.Utils.isRawSql(data[0][prop.name]));
|
|
281
352
|
const returningFields = core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames));
|
|
282
353
|
/* istanbul ignore next */
|
|
283
354
|
sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
|
|
@@ -289,8 +360,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
289
360
|
pk = data.map(d => core_1.Utils.getPrimaryKeyCond(d, pks));
|
|
290
361
|
}
|
|
291
362
|
else {
|
|
292
|
-
res.row
|
|
293
|
-
res.rows
|
|
363
|
+
res.row ??= {};
|
|
364
|
+
res.rows ??= [];
|
|
294
365
|
pk = data.map((d, i) => d[pks[0]] ?? res.rows[i]?.[pks[0]]).map(d => [d]);
|
|
295
366
|
res.insertId = res.insertId || res.row[pks[0]];
|
|
296
367
|
}
|
|
@@ -300,7 +371,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
300
371
|
return res;
|
|
301
372
|
}
|
|
302
373
|
async nativeUpdate(entityName, where, data, options = {}) {
|
|
303
|
-
options.convertCustomTypes
|
|
374
|
+
options.convertCustomTypes ??= true;
|
|
304
375
|
const meta = this.metadata.find(entityName);
|
|
305
376
|
const pks = this.getPrimaryKeyFields(entityName);
|
|
306
377
|
const collections = this.extractManyToMany(entityName, data);
|
|
@@ -314,11 +385,18 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
314
385
|
.withSchema(this.getSchemaName(meta, options));
|
|
315
386
|
if (options.upsert) {
|
|
316
387
|
/* istanbul ignore next */
|
|
317
|
-
const uniqueFields = core_1.Utils.isPlainObject(where) ?
|
|
318
|
-
|
|
388
|
+
const uniqueFields = options.onConflictFields ?? (core_1.Utils.isPlainObject(where) ? core_1.Utils.keys(where) : meta.primaryKeys);
|
|
389
|
+
const returning = (0, core_1.getOnConflictReturningFields)(meta, data, uniqueFields, options);
|
|
319
390
|
qb.insert(data)
|
|
320
|
-
.onConflict(uniqueFields
|
|
321
|
-
.
|
|
391
|
+
.onConflict(uniqueFields)
|
|
392
|
+
.returning(returning);
|
|
393
|
+
if (!options.onConflictAction || options.onConflictAction === 'merge') {
|
|
394
|
+
const fields = (0, core_1.getOnConflictFields)(data, uniqueFields, options);
|
|
395
|
+
qb.merge(fields);
|
|
396
|
+
}
|
|
397
|
+
if (options.onConflictAction === 'ignore') {
|
|
398
|
+
qb.ignore();
|
|
399
|
+
}
|
|
322
400
|
}
|
|
323
401
|
else {
|
|
324
402
|
qb.update(data).where(where);
|
|
@@ -331,20 +409,36 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
331
409
|
return res;
|
|
332
410
|
}
|
|
333
411
|
async nativeUpdateMany(entityName, where, data, options = {}) {
|
|
334
|
-
options.processCollections
|
|
335
|
-
options.convertCustomTypes
|
|
412
|
+
options.processCollections ??= true;
|
|
413
|
+
options.convertCustomTypes ??= true;
|
|
336
414
|
const meta = this.metadata.get(entityName);
|
|
337
415
|
if (options.upsert) {
|
|
338
|
-
const uniqueFields = core_1.Utils.isPlainObject(where[0]) ? Object.keys(where[0]) : meta.primaryKeys;
|
|
416
|
+
const uniqueFields = options.onConflictFields ?? (core_1.Utils.isPlainObject(where[0]) ? Object.keys(where[0]) : meta.primaryKeys);
|
|
339
417
|
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
|
|
418
|
+
const returning = (0, core_1.getOnConflictReturningFields)(meta, data[0], uniqueFields, options);
|
|
340
419
|
qb.insert(data)
|
|
341
|
-
.onConflict(uniqueFields
|
|
342
|
-
.
|
|
420
|
+
.onConflict(uniqueFields)
|
|
421
|
+
.returning(returning);
|
|
422
|
+
if (!options.onConflictAction || options.onConflictAction === 'merge') {
|
|
423
|
+
const fields = (0, core_1.getOnConflictFields)(data[0], uniqueFields, options);
|
|
424
|
+
qb.merge(fields);
|
|
425
|
+
}
|
|
426
|
+
if (options.onConflictAction === 'ignore') {
|
|
427
|
+
qb.ignore();
|
|
428
|
+
}
|
|
343
429
|
return qb.execute('run', false);
|
|
344
430
|
}
|
|
345
431
|
const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
|
|
346
432
|
const keys = new Set();
|
|
347
|
-
|
|
433
|
+
const returning = new Set();
|
|
434
|
+
data.forEach(row => {
|
|
435
|
+
core_1.Utils.keys(row).forEach(k => {
|
|
436
|
+
keys.add(k);
|
|
437
|
+
if (core_1.Utils.isRawSql(row[k])) {
|
|
438
|
+
returning.add(k);
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
});
|
|
348
442
|
const pkCond = core_1.Utils.flatten(meta.primaryKeys.map(pk => meta.properties[pk].fieldNames)).map(pk => `${this.platform.quoteIdentifier(pk)} = ?`).join(' and ');
|
|
349
443
|
const params = [];
|
|
350
444
|
let sql = `update ${this.getTableName(meta, options)} set `;
|
|
@@ -373,7 +467,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
373
467
|
const versionProperty = meta.properties[meta.versionProperty];
|
|
374
468
|
const quotedFieldName = this.platform.quoteIdentifier(versionProperty.fieldNames[0]);
|
|
375
469
|
sql += `${quotedFieldName} = `;
|
|
376
|
-
if (versionProperty.
|
|
470
|
+
if (versionProperty.runtimeType === 'Date') {
|
|
377
471
|
sql += this.platform.getCurrentTimestampSQL(versionProperty.length);
|
|
378
472
|
}
|
|
379
473
|
else {
|
|
@@ -404,6 +498,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
404
498
|
return '?';
|
|
405
499
|
});
|
|
406
500
|
sql += ` in (${conds.join(', ')})`;
|
|
501
|
+
if (this.platform.usesReturningStatement() && returning.size > 0) {
|
|
502
|
+
const returningFields = core_1.Utils.flatten([...returning].map(prop => meta.properties[prop].fieldNames));
|
|
503
|
+
/* istanbul ignore next */
|
|
504
|
+
sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
|
|
505
|
+
}
|
|
407
506
|
const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx));
|
|
408
507
|
for (let i = 0; i < collections.length; i++) {
|
|
409
508
|
await this.processManyToMany(meta, where[i], collections[i], false, options);
|
|
@@ -438,30 +537,53 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
438
537
|
deleteDiff.push(...snapshot);
|
|
439
538
|
insertDiff.push(...current);
|
|
440
539
|
}
|
|
441
|
-
if (coll.property.
|
|
540
|
+
if (coll.property.kind === core_1.ReferenceKind.ONE_TO_MANY) {
|
|
442
541
|
const cols = coll.property.referencedColumnNames;
|
|
443
542
|
const qb = this.createQueryBuilder(coll.property.type, ctx, 'write')
|
|
444
|
-
.withSchema(this.getSchemaName(meta, options))
|
|
445
|
-
|
|
543
|
+
.withSchema(this.getSchemaName(meta, options));
|
|
544
|
+
if (coll.getSnapshot() === undefined) {
|
|
545
|
+
if (coll.property.orphanRemoval) {
|
|
546
|
+
const kqb = qb.delete({ [coll.property.mappedBy]: pks })
|
|
547
|
+
.getKnexQuery()
|
|
548
|
+
.whereNotIn(cols, insertDiff);
|
|
549
|
+
return this.rethrow(this.execute(kqb));
|
|
550
|
+
}
|
|
551
|
+
const kqb = qb.update({ [coll.property.mappedBy]: null })
|
|
552
|
+
.getKnexQuery()
|
|
553
|
+
.whereNotIn(cols, insertDiff);
|
|
554
|
+
return this.rethrow(this.execute(kqb));
|
|
555
|
+
}
|
|
556
|
+
const kqb = qb.update({ [coll.property.mappedBy]: pks })
|
|
446
557
|
.getKnexQuery()
|
|
447
558
|
.whereIn(cols, insertDiff);
|
|
448
|
-
return this.rethrow(this.execute(
|
|
559
|
+
return this.rethrow(this.execute(kqb));
|
|
449
560
|
}
|
|
450
561
|
/* istanbul ignore next */
|
|
451
562
|
const ownerSchema = wrapped.getSchema() === '*' ? this.config.get('schema') : wrapped.getSchema();
|
|
452
563
|
const pivotMeta = this.metadata.find(coll.property.pivotEntity);
|
|
453
564
|
if (pivotMeta.schema === '*') {
|
|
454
565
|
/* istanbul ignore next */
|
|
455
|
-
options
|
|
566
|
+
options ??= {};
|
|
456
567
|
options.schema = ownerSchema;
|
|
457
568
|
}
|
|
458
569
|
return this.rethrow(this.updateCollectionDiff(meta, coll.property, pks, deleteDiff, insertDiff, options));
|
|
459
570
|
}
|
|
460
571
|
async loadFromPivotTable(prop, owners, where = {}, orderBy, ctx, options) {
|
|
461
572
|
const pivotProp2 = this.getPivotInverseProperty(prop);
|
|
573
|
+
const prop2 = prop.targetMeta.properties[prop.mappedBy ?? prop.inversedBy];
|
|
462
574
|
const ownerMeta = this.metadata.find(pivotProp2.type);
|
|
463
575
|
const pivotMeta = this.metadata.find(prop.pivotEntity);
|
|
464
|
-
|
|
576
|
+
options = { ...options };
|
|
577
|
+
const qb = this.createQueryBuilder(prop.type, ctx, options.connectionType)
|
|
578
|
+
.withSchema(this.getSchemaName(prop.targetMeta, options))
|
|
579
|
+
.indexHint(options.indexHint)
|
|
580
|
+
.comment(options.comments)
|
|
581
|
+
.hintComment(options.hintComments);
|
|
582
|
+
const pivotAlias = qb.getNextAlias(pivotMeta.tableName);
|
|
583
|
+
const pivotKey = pivotProp2.joinColumns.map(column => `${pivotAlias}.${column}`).join(core_1.Utils.PK_SEPARATOR);
|
|
584
|
+
const cond = {
|
|
585
|
+
[pivotKey]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) },
|
|
586
|
+
};
|
|
465
587
|
/* istanbul ignore if */
|
|
466
588
|
if (!core_1.Utils.isEmpty(where) && Object.keys(where).every(k => core_1.Utils.isOperator(k, false))) {
|
|
467
589
|
where = cond;
|
|
@@ -469,22 +591,32 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
469
591
|
else {
|
|
470
592
|
where = { ...where, ...cond };
|
|
471
593
|
}
|
|
472
|
-
orderBy = this.getPivotOrderBy(prop, orderBy);
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
const
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
594
|
+
orderBy = this.getPivotOrderBy(prop, pivotAlias, orderBy);
|
|
595
|
+
const populate = this.autoJoinOneToOneOwner(prop.targetMeta, []);
|
|
596
|
+
const fields = this.buildFields(prop.targetMeta, (options.populate ?? []), [], qb, options.fields);
|
|
597
|
+
const k1 = !prop.owner ? 'joinColumns' : 'inverseJoinColumns';
|
|
598
|
+
const k2 = prop.owner ? 'joinColumns' : 'inverseJoinColumns';
|
|
599
|
+
const cols = [
|
|
600
|
+
...prop[k1].map(col => `${pivotAlias}.${col} as fk__${col}`),
|
|
601
|
+
...prop[k2].map(col => `${pivotAlias}.${col} as fk__${col}`),
|
|
602
|
+
];
|
|
603
|
+
fields.push(...cols);
|
|
604
|
+
qb.select(fields)
|
|
605
|
+
.join(prop2.name, pivotAlias, {}, 'pivotJoin', undefined, options.schema)
|
|
606
|
+
.populate(populate)
|
|
607
|
+
.where(where)
|
|
608
|
+
.orderBy(orderBy)
|
|
609
|
+
.setLockMode(options.lockMode, options.lockTableAliases);
|
|
610
|
+
if (owners.length === 1 && (options.offset != null || options.limit != null)) {
|
|
480
611
|
qb.limit(options.limit, options.offset);
|
|
481
612
|
}
|
|
482
|
-
|
|
613
|
+
const schema = this.config.get('schema');
|
|
614
|
+
if (prop.targetMeta.schema !== '*' && pivotMeta.schema === '*' && options.schema) {
|
|
483
615
|
// eslint-disable-next-line dot-notation
|
|
484
616
|
qb['finalize']();
|
|
485
617
|
// eslint-disable-next-line dot-notation
|
|
486
618
|
Object.values(qb['_joins']).forEach(join => {
|
|
487
|
-
join.schema =
|
|
619
|
+
join.schema = schema;
|
|
488
620
|
});
|
|
489
621
|
}
|
|
490
622
|
const items = owners.length ? await this.rethrow(qb.execute('all')) : [];
|
|
@@ -508,6 +640,18 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
508
640
|
});
|
|
509
641
|
return map;
|
|
510
642
|
}
|
|
643
|
+
getPivotOrderBy(prop, pivotAlias, orderBy) {
|
|
644
|
+
if (!core_1.Utils.isEmpty(orderBy)) {
|
|
645
|
+
return orderBy;
|
|
646
|
+
}
|
|
647
|
+
if (!core_1.Utils.isEmpty(prop.orderBy)) {
|
|
648
|
+
return core_1.Utils.asArray(prop.orderBy);
|
|
649
|
+
}
|
|
650
|
+
if (prop.fixedOrder) {
|
|
651
|
+
return [{ [`${pivotAlias}.${prop.fixedOrderColumn}`]: core_1.QueryOrder.ASC }];
|
|
652
|
+
}
|
|
653
|
+
return [];
|
|
654
|
+
}
|
|
511
655
|
async execute(queryOrKnex, params = [], method = 'all', ctx) {
|
|
512
656
|
return this.rethrow(this.connection.execute(queryOrKnex, params, method, ctx));
|
|
513
657
|
}
|
|
@@ -520,7 +664,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
520
664
|
}
|
|
521
665
|
const relationsToPopulate = populate.map(({ field }) => field);
|
|
522
666
|
const toPopulate = meta.relations
|
|
523
|
-
.filter(prop => prop.
|
|
667
|
+
.filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
|
|
524
668
|
.filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
|
|
525
669
|
.map(prop => ({ field: prop.name, strategy: prop.strategy }));
|
|
526
670
|
return [...populate, ...toPopulate];
|
|
@@ -528,23 +672,30 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
528
672
|
joinedProps(meta, populate) {
|
|
529
673
|
return populate.filter(p => {
|
|
530
674
|
const prop = meta.properties[p.field] || {};
|
|
531
|
-
return (p.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && prop.
|
|
675
|
+
return (p.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && prop.kind !== core_1.ReferenceKind.SCALAR;
|
|
532
676
|
});
|
|
533
677
|
}
|
|
534
678
|
/**
|
|
535
679
|
* @internal
|
|
536
680
|
*/
|
|
537
|
-
mergeJoinedResult(rawResults, meta) {
|
|
681
|
+
mergeJoinedResult(rawResults, meta, joinedProps) {
|
|
538
682
|
// group by the root entity primary key first
|
|
539
683
|
const map = {};
|
|
540
684
|
const res = [];
|
|
541
685
|
rawResults.forEach(item => {
|
|
542
686
|
const pk = core_1.Utils.getCompositeKeyHash(item, meta);
|
|
543
687
|
if (map[pk]) {
|
|
544
|
-
|
|
688
|
+
joinedProps.forEach(hint => {
|
|
689
|
+
// Sometimes we might join a M:N relation with additional filter on the target entity, and as a result, we get
|
|
690
|
+
// the first result with `null` for all target values, which is mapped as empty array. When we see that happen,
|
|
691
|
+
// we need to merge the results of the next item.
|
|
692
|
+
if (Array.isArray(map[pk][hint.field]) && Array.isArray(item[hint.field]) && map[pk][hint.field].length === 0) {
|
|
693
|
+
item[hint.field].forEach((el) => map[pk][hint.field].push(el));
|
|
694
|
+
}
|
|
695
|
+
});
|
|
545
696
|
}
|
|
546
697
|
else {
|
|
547
|
-
map[pk] =
|
|
698
|
+
map[pk] = item;
|
|
548
699
|
res.push(item);
|
|
549
700
|
}
|
|
550
701
|
});
|
|
@@ -553,14 +704,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
553
704
|
getFieldsForJoinedLoad(qb, meta, explicitFields, populate = [], parentTableAlias, parentJoinPath) {
|
|
554
705
|
const fields = [];
|
|
555
706
|
const joinedProps = this.joinedProps(meta, populate);
|
|
707
|
+
if (explicitFields?.includes('*')) {
|
|
708
|
+
fields.push('*');
|
|
709
|
+
}
|
|
556
710
|
const shouldHaveColumn = (prop, populate, fields) => {
|
|
557
711
|
if (!this.platform.shouldHaveColumn(prop, populate)) {
|
|
558
712
|
return false;
|
|
559
713
|
}
|
|
560
714
|
if (!fields || prop.primary) {
|
|
561
|
-
return
|
|
715
|
+
return !fields?.includes('*');
|
|
562
716
|
}
|
|
563
|
-
return fields.
|
|
717
|
+
return fields.some(f => f === prop.name || f.toString().startsWith(prop.name + '.'));
|
|
564
718
|
};
|
|
565
719
|
// alias all fields in the primary table
|
|
566
720
|
meta.props
|
|
@@ -587,24 +741,25 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
587
741
|
* @internal
|
|
588
742
|
*/
|
|
589
743
|
mapPropToFieldNames(qb, prop, tableAlias) {
|
|
590
|
-
const
|
|
744
|
+
const knex = this.connection.getKnex();
|
|
745
|
+
const aliased = knex.ref(tableAlias ? `${tableAlias}__${prop.fieldNames[0]}` : prop.fieldNames[0]).toString();
|
|
591
746
|
if (prop.customType?.convertToJSValueSQL && tableAlias) {
|
|
592
|
-
const prefixed =
|
|
593
|
-
return [
|
|
747
|
+
const prefixed = knex.ref(prop.fieldNames[0]).withSchema(tableAlias).toString();
|
|
748
|
+
return [(0, core_1.raw)(`${prop.customType.convertToJSValueSQL(prefixed, this.platform)} as ${aliased}`)];
|
|
594
749
|
}
|
|
595
750
|
if (prop.formula) {
|
|
596
|
-
const alias =
|
|
597
|
-
return [`${prop.formula(alias)} as ${aliased}`];
|
|
751
|
+
const alias = knex.ref(tableAlias ?? qb.alias).toString();
|
|
752
|
+
return [(0, core_1.raw)(`${prop.formula(alias)} as ${aliased}`)];
|
|
598
753
|
}
|
|
599
754
|
if (tableAlias) {
|
|
600
|
-
return prop.fieldNames.map(fieldName =>
|
|
755
|
+
return prop.fieldNames.map(fieldName => knex.ref(fieldName).withSchema(tableAlias).as(`${tableAlias}__${fieldName}`));
|
|
601
756
|
}
|
|
602
757
|
return prop.fieldNames;
|
|
603
758
|
}
|
|
604
759
|
/** @internal */
|
|
605
|
-
createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes) {
|
|
760
|
+
createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes, logging) {
|
|
606
761
|
const connectionType = this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
|
|
607
|
-
const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType);
|
|
762
|
+
const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType, undefined, logging);
|
|
608
763
|
if (!convertCustomTypes) {
|
|
609
764
|
qb.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
|
|
610
765
|
}
|
|
@@ -628,7 +783,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
628
783
|
}
|
|
629
784
|
const ret = {};
|
|
630
785
|
this.metadata.find(entityName).relations.forEach(prop => {
|
|
631
|
-
if (prop.
|
|
786
|
+
if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && data[prop.name]) {
|
|
632
787
|
ret[prop.name] = data[prop.name].map((item) => core_1.Utils.asArray(item));
|
|
633
788
|
delete data[prop.name];
|
|
634
789
|
}
|
|
@@ -649,10 +804,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
649
804
|
if (!deleteDiff) {
|
|
650
805
|
deleteDiff = [];
|
|
651
806
|
}
|
|
807
|
+
const pivotMeta = this.metadata.find(prop.pivotEntity);
|
|
652
808
|
if (deleteDiff === true || deleteDiff.length > 0) {
|
|
653
|
-
const qb1 = this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
|
|
654
|
-
.withSchema(this.getSchemaName(meta, options))
|
|
655
|
-
.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
|
|
809
|
+
const qb1 = this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write').withSchema(this.getSchemaName(pivotMeta, options));
|
|
656
810
|
const knex = qb1.getKnex();
|
|
657
811
|
if (Array.isArray(deleteDiff)) {
|
|
658
812
|
knex.whereIn(prop.inverseJoinColumns, deleteDiff);
|
|
@@ -671,13 +825,16 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
671
825
|
});
|
|
672
826
|
/* istanbul ignore else */
|
|
673
827
|
if (this.platform.allowsMultiInsert()) {
|
|
674
|
-
await this.nativeInsertMany(prop.pivotEntity, items, {
|
|
828
|
+
await this.nativeInsertMany(prop.pivotEntity, items, {
|
|
829
|
+
...options,
|
|
830
|
+
convertCustomTypes: false,
|
|
831
|
+
processCollections: false,
|
|
832
|
+
});
|
|
675
833
|
}
|
|
676
834
|
else {
|
|
677
835
|
await core_1.Utils.runSerial(items, item => {
|
|
678
836
|
return this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
|
|
679
|
-
.
|
|
680
|
-
.withSchema(this.getSchemaName(meta, options))
|
|
837
|
+
.withSchema(this.getSchemaName(pivotMeta, options))
|
|
681
838
|
.insert(item)
|
|
682
839
|
.execute('run', false);
|
|
683
840
|
});
|
|
@@ -685,9 +842,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
685
842
|
}
|
|
686
843
|
async lockPessimistic(entity, options) {
|
|
687
844
|
const meta = (0, core_1.helper)(entity).__meta;
|
|
688
|
-
const qb = this.createQueryBuilder(entity.constructor.name, options.ctx).
|
|
845
|
+
const qb = this.createQueryBuilder(entity.constructor.name, options.ctx).withSchema(options.schema ?? meta.schema);
|
|
689
846
|
const cond = core_1.Utils.getPrimaryKeyCond(entity, meta.primaryKeys);
|
|
690
|
-
qb.select('1').where(cond).setLockMode(options.lockMode, options.lockTableAliases);
|
|
847
|
+
qb.select((0, core_1.raw)('1')).where(cond).setLockMode(options.lockMode, options.lockTableAliases);
|
|
691
848
|
await this.rethrow(qb.execute());
|
|
692
849
|
}
|
|
693
850
|
buildJoinedPropsOrderBy(entityName, qb, meta, populate, parentPath) {
|
|
@@ -699,7 +856,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
699
856
|
const path = `${parentPath ? parentPath : entityName}.${relation.field}`;
|
|
700
857
|
const propAlias = qb.getAliasForJoinPath(path);
|
|
701
858
|
if (propOrderBy) {
|
|
702
|
-
|
|
859
|
+
core_1.Utils.keys(propOrderBy).forEach(field => {
|
|
703
860
|
orderBy.push({ [`${propAlias}.${field}`]: propOrderBy[field] });
|
|
704
861
|
});
|
|
705
862
|
}
|
|
@@ -726,12 +883,12 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
726
883
|
return ret;
|
|
727
884
|
}
|
|
728
885
|
processField(meta, prop, field, ret, populate, joinedProps, qb) {
|
|
729
|
-
if (!prop || (prop.
|
|
886
|
+
if (!prop || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner)) {
|
|
730
887
|
return;
|
|
731
888
|
}
|
|
732
|
-
if (prop.
|
|
889
|
+
if (prop.kind === core_1.ReferenceKind.EMBEDDED) {
|
|
733
890
|
if (prop.object) {
|
|
734
|
-
ret.push(prop.
|
|
891
|
+
ret.push(prop.name);
|
|
735
892
|
return;
|
|
736
893
|
}
|
|
737
894
|
const parts = field.split('.');
|
|
@@ -743,9 +900,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
743
900
|
}
|
|
744
901
|
return;
|
|
745
902
|
}
|
|
746
|
-
|
|
747
|
-
ret.push(prop.fieldNames[0]);
|
|
748
|
-
}
|
|
903
|
+
ret.push(prop.name);
|
|
749
904
|
}
|
|
750
905
|
buildFields(meta, populate, joinedProps, qb, fields) {
|
|
751
906
|
const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => p.field === prop.name || p.all));
|
|
@@ -753,6 +908,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
753
908
|
const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL);
|
|
754
909
|
const hasExplicitFields = !!fields;
|
|
755
910
|
const ret = [];
|
|
911
|
+
let addFormulas = false;
|
|
756
912
|
if (joinedProps.length > 0) {
|
|
757
913
|
ret.push(...this.getFieldsForJoinedLoad(qb, meta, fields, populate));
|
|
758
914
|
}
|
|
@@ -778,20 +934,22 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
778
934
|
else if (lazyProps.filter(p => !p.formula).length > 0) {
|
|
779
935
|
const props = meta.props.filter(prop => this.platform.shouldHaveColumn(prop, populate, false));
|
|
780
936
|
ret.push(...core_1.Utils.flatten(props.filter(p => !lazyProps.includes(p)).map(p => p.fieldNames)));
|
|
937
|
+
addFormulas = true;
|
|
781
938
|
}
|
|
782
939
|
else if (hasLazyFormulas || requiresSQLConversion) {
|
|
783
940
|
ret.push('*');
|
|
941
|
+
addFormulas = true;
|
|
784
942
|
}
|
|
785
|
-
if (ret.length > 0 && !hasExplicitFields) {
|
|
943
|
+
if (ret.length > 0 && !hasExplicitFields && addFormulas) {
|
|
786
944
|
meta.props
|
|
787
945
|
.filter(prop => prop.formula && !lazyProps.includes(prop))
|
|
788
946
|
.forEach(prop => {
|
|
789
|
-
const alias =
|
|
790
|
-
const aliased =
|
|
791
|
-
ret.push(`${prop.formula(alias)} as ${aliased}`);
|
|
947
|
+
const alias = this.connection.getKnex().ref(qb.alias).toString();
|
|
948
|
+
const aliased = this.connection.getKnex().ref(prop.fieldNames[0]).toString();
|
|
949
|
+
ret.push((0, core_1.raw)(`${prop.formula(alias)} as ${aliased}`));
|
|
792
950
|
});
|
|
793
951
|
meta.props
|
|
794
|
-
.filter(prop => prop.
|
|
952
|
+
.filter(prop => prop.hasConvertToDatabaseValueSQL || prop.hasConvertToJSValueSQL)
|
|
795
953
|
.forEach(prop => ret.push(prop.name));
|
|
796
954
|
}
|
|
797
955
|
return ret.length > 0 ? core_1.Utils.unique(ret) : ['*'];
|