@mikro-orm/knex 7.0.0-dev.9 → 7.0.0-dev.90
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 +11 -5
- package/AbstractSqlConnection.js +78 -32
- package/AbstractSqlDriver.d.ts +9 -5
- package/AbstractSqlDriver.js +267 -227
- package/AbstractSqlPlatform.js +5 -5
- package/PivotCollectionPersister.d.ts +8 -4
- package/PivotCollectionPersister.js +55 -31
- package/README.md +3 -2
- package/SqlEntityManager.d.ts +10 -2
- package/SqlEntityManager.js +11 -2
- package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +2 -0
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +42 -3
- package/dialects/mysql/MySqlExceptionConverter.d.ts +3 -3
- package/dialects/mysql/MySqlExceptionConverter.js +4 -5
- package/dialects/mysql/MySqlSchemaHelper.js +2 -2
- package/dialects/postgresql/PostgreSqlTableCompiler.d.ts +1 -0
- package/dialects/postgresql/PostgreSqlTableCompiler.js +1 -0
- package/dialects/sqlite/BaseSqliteConnection.d.ts +3 -2
- package/dialects/sqlite/BaseSqliteConnection.js +2 -14
- package/dialects/sqlite/BaseSqlitePlatform.js +1 -2
- package/dialects/sqlite/SqliteExceptionConverter.d.ts +2 -2
- package/dialects/sqlite/SqliteExceptionConverter.js +6 -4
- package/dialects/sqlite/SqliteSchemaHelper.js +5 -6
- package/index.d.ts +2 -1
- package/index.js +2 -1
- package/package.json +5 -5
- package/plugin/index.d.ts +53 -0
- package/plugin/index.js +42 -0
- package/plugin/transformer.d.ts +115 -0
- package/plugin/transformer.js +883 -0
- package/query/ArrayCriteriaNode.d.ts +1 -0
- package/query/ArrayCriteriaNode.js +3 -0
- package/query/CriteriaNode.d.ts +4 -5
- package/query/CriteriaNode.js +13 -9
- package/query/CriteriaNodeFactory.js +12 -7
- package/query/NativeQueryBuilder.js +1 -1
- package/query/ObjectCriteriaNode.d.ts +1 -0
- package/query/ObjectCriteriaNode.js +35 -8
- package/query/QueryBuilder.d.ts +59 -10
- package/query/QueryBuilder.js +166 -50
- package/query/QueryBuilderHelper.d.ts +1 -1
- package/query/QueryBuilderHelper.js +20 -14
- package/query/ScalarCriteriaNode.d.ts +3 -3
- package/query/ScalarCriteriaNode.js +9 -7
- package/query/index.d.ts +1 -0
- package/query/index.js +1 -0
- package/query/raw.d.ts +59 -0
- package/query/raw.js +68 -0
- package/query/rawKnex.d.ts +58 -0
- package/query/rawKnex.js +72 -0
- package/schema/DatabaseSchema.js +25 -4
- package/schema/DatabaseTable.d.ts +5 -4
- package/schema/DatabaseTable.js +65 -34
- package/schema/SchemaComparator.js +5 -6
- package/schema/SchemaHelper.d.ts +2 -0
- package/schema/SchemaHelper.js +14 -10
- package/schema/SqlSchemaGenerator.d.ts +13 -6
- package/schema/SqlSchemaGenerator.js +41 -20
- package/typings.d.ts +85 -3
package/AbstractSqlDriver.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ALIAS_REPLACEMENT_RE, DatabaseDriver, EntityManagerType, getOnConflictFields, getOnConflictReturningFields, helper, isRaw, LoadStrategy, parseJsonSafe, QueryFlag, QueryHelper, QueryOrder, raw, RawQueryFragment, ReferenceKind, Utils, } from '@mikro-orm/core';
|
|
1
|
+
import { ALIAS_REPLACEMENT_RE, DatabaseDriver, EntityManagerType, getLoadingStrategy, getOnConflictFields, getOnConflictReturningFields, helper, isRaw, LoadStrategy, parseJsonSafe, QueryFlag, QueryHelper, QueryOrder, raw, RawQueryFragment, ReferenceKind, Utils, } from '@mikro-orm/core';
|
|
2
2
|
import { QueryBuilder } from './query/QueryBuilder.js';
|
|
3
3
|
import { JoinType, QueryType } from './query/enums.js';
|
|
4
4
|
import { SqlEntityManager } from './SqlEntityManager.js';
|
|
@@ -21,15 +21,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
21
21
|
const EntityManagerClass = this.config.get('entityManager', SqlEntityManager);
|
|
22
22
|
return new EntityManagerClass(this.config, this, this.metadata, useContext);
|
|
23
23
|
}
|
|
24
|
-
async
|
|
25
|
-
|
|
26
|
-
const meta = this.metadata.find(entityName);
|
|
27
|
-
if (meta?.virtual) {
|
|
28
|
-
return this.findVirtual(entityName, where, options);
|
|
29
|
-
}
|
|
24
|
+
async createQueryBuilderFromOptions(meta, where, options = {}) {
|
|
25
|
+
const connectionType = this.resolveConnectionType({ ctx: options.ctx, connectionType: options.connectionType });
|
|
30
26
|
const populate = this.autoJoinOneToOneOwner(meta, options.populate, options.fields);
|
|
31
27
|
const joinedProps = this.joinedProps(meta, populate, options);
|
|
32
|
-
const qb = this.createQueryBuilder(
|
|
28
|
+
const qb = this.createQueryBuilder(meta.className, options.ctx, connectionType, false, options.logging, undefined, options.em);
|
|
33
29
|
const fields = this.buildFields(meta, populate, joinedProps, qb, qb.alias, options);
|
|
34
30
|
const orderBy = this.buildOrderBy(qb, meta, populate, options);
|
|
35
31
|
const populateWhere = this.buildPopulateWhere(meta, joinedProps, options);
|
|
@@ -66,8 +62,17 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
66
62
|
if (options.em) {
|
|
67
63
|
await qb.applyJoinedFilters(options.em, options.filters);
|
|
68
64
|
}
|
|
65
|
+
return qb;
|
|
66
|
+
}
|
|
67
|
+
async find(entityName, where, options = {}) {
|
|
68
|
+
options = { populate: [], orderBy: [], ...options };
|
|
69
|
+
const meta = this.metadata.find(entityName);
|
|
70
|
+
if (meta?.virtual) {
|
|
71
|
+
return this.findVirtual(entityName, where, options);
|
|
72
|
+
}
|
|
73
|
+
const qb = await this.createQueryBuilderFromOptions(meta, where, options);
|
|
69
74
|
const result = await this.rethrow(qb.execute('all'));
|
|
70
|
-
if (
|
|
75
|
+
if (options.last && !options.first) {
|
|
71
76
|
result.reverse();
|
|
72
77
|
}
|
|
73
78
|
return result;
|
|
@@ -107,7 +112,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
107
112
|
}
|
|
108
113
|
async findFromVirtual(entityName, where, options, type) {
|
|
109
114
|
const meta = this.metadata.get(entityName);
|
|
110
|
-
/* v8 ignore next
|
|
115
|
+
/* v8 ignore next */
|
|
111
116
|
if (!meta.expression) {
|
|
112
117
|
return type === QueryType.SELECT ? [] : 0;
|
|
113
118
|
}
|
|
@@ -130,32 +135,46 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
130
135
|
/* v8 ignore next */
|
|
131
136
|
return res;
|
|
132
137
|
}
|
|
133
|
-
async
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
qb.where(where);
|
|
139
|
-
const { first, last, before, after } = options;
|
|
140
|
-
const isCursorPagination = [first, last, before, after].some(v => v != null);
|
|
141
|
-
if (type !== QueryType.COUNT) {
|
|
142
|
-
if (options.orderBy) {
|
|
143
|
-
if (isCursorPagination) {
|
|
144
|
-
const { orderBy: newOrderBy, where } = this.processCursorOptions(meta, options, options.orderBy);
|
|
145
|
-
qb.andWhere(where).orderBy(newOrderBy);
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
qb.orderBy(options.orderBy);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
qb.limit(options?.limit, options?.offset);
|
|
138
|
+
async *streamFromVirtual(entityName, where, options) {
|
|
139
|
+
const meta = this.metadata.get(entityName);
|
|
140
|
+
/* v8 ignore next */
|
|
141
|
+
if (!meta.expression) {
|
|
142
|
+
return;
|
|
152
143
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
144
|
+
if (typeof meta.expression === 'string') {
|
|
145
|
+
yield* this.wrapVirtualExpressionInSubqueryStream(meta, meta.expression, where, options, QueryType.SELECT);
|
|
146
|
+
return;
|
|
156
147
|
}
|
|
157
|
-
|
|
158
|
-
|
|
148
|
+
const em = this.createEntityManager();
|
|
149
|
+
em.setTransactionContext(options.ctx);
|
|
150
|
+
const res = meta.expression(em, where, options, true);
|
|
151
|
+
if (typeof res === 'string') {
|
|
152
|
+
yield* this.wrapVirtualExpressionInSubqueryStream(meta, res, where, options, QueryType.SELECT);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (res instanceof QueryBuilder) {
|
|
156
|
+
yield* this.wrapVirtualExpressionInSubqueryStream(meta, res.getFormattedQuery(), where, options, QueryType.SELECT);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (res instanceof RawQueryFragment) {
|
|
160
|
+
const expr = this.platform.formatQuery(res.sql, res.params);
|
|
161
|
+
yield* this.wrapVirtualExpressionInSubqueryStream(meta, expr, where, options, QueryType.SELECT);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
/* v8 ignore next */
|
|
165
|
+
yield* res;
|
|
166
|
+
}
|
|
167
|
+
async wrapVirtualExpressionInSubquery(meta, expression, where, options, type) {
|
|
168
|
+
const qb = await this.createQueryBuilderFromOptions(meta, where, options);
|
|
169
|
+
qb.setFlag(QueryFlag.DISABLE_PAGINATE);
|
|
170
|
+
const isCursorPagination = [options.first, options.last, options.before, options.after].some(v => v != null);
|
|
171
|
+
const native = qb.getNativeQuery(false);
|
|
172
|
+
if (type === QueryType.COUNT) {
|
|
173
|
+
native
|
|
174
|
+
.clear('select')
|
|
175
|
+
.clear('limit')
|
|
176
|
+
.clear('offset')
|
|
177
|
+
.count();
|
|
159
178
|
}
|
|
160
179
|
native.from(raw(`(${expression}) as ${this.platform.quoteIdentifier(qb.alias)}`));
|
|
161
180
|
const query = native.compile();
|
|
@@ -163,14 +182,26 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
163
182
|
if (type === QueryType.COUNT) {
|
|
164
183
|
return res[0].count;
|
|
165
184
|
}
|
|
166
|
-
if (isCursorPagination && !first && !!last) {
|
|
185
|
+
if (isCursorPagination && !options.first && !!options.last) {
|
|
167
186
|
res.reverse();
|
|
168
187
|
}
|
|
169
188
|
return res.map(row => this.mapResult(row, meta));
|
|
170
189
|
}
|
|
190
|
+
async *wrapVirtualExpressionInSubqueryStream(meta, expression, where, options, type) {
|
|
191
|
+
const qb = await this.createQueryBuilderFromOptions(meta, where, options);
|
|
192
|
+
qb.unsetFlag(QueryFlag.DISABLE_PAGINATE);
|
|
193
|
+
const native = qb.getNativeQuery(false);
|
|
194
|
+
native.from(raw(`(${expression}) as ${this.platform.quoteIdentifier(qb.alias)}`));
|
|
195
|
+
const query = native.compile();
|
|
196
|
+
const connectionType = this.resolveConnectionType({ ctx: options.ctx, connectionType: options.connectionType });
|
|
197
|
+
const res = this.getConnection(connectionType).stream(query.sql, query.params, options.ctx, options.loggerContext);
|
|
198
|
+
for await (const row of res) {
|
|
199
|
+
yield this.mapResult(row, meta);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
171
202
|
mapResult(result, meta, populate = [], qb, map = {}) {
|
|
172
203
|
const ret = super.mapResult(result, meta);
|
|
173
|
-
/* v8 ignore next
|
|
204
|
+
/* v8 ignore next */
|
|
174
205
|
if (!ret) {
|
|
175
206
|
return null;
|
|
176
207
|
}
|
|
@@ -185,7 +216,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
185
216
|
joinedProps.forEach(hint => {
|
|
186
217
|
const [propName, ref] = hint.field.split(':', 2);
|
|
187
218
|
const prop = meta.properties[propName];
|
|
188
|
-
/* v8 ignore next
|
|
219
|
+
/* v8 ignore next */
|
|
189
220
|
if (!prop) {
|
|
190
221
|
return;
|
|
191
222
|
}
|
|
@@ -199,7 +230,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
199
230
|
path += '[pivot]';
|
|
200
231
|
}
|
|
201
232
|
const relationAlias = qb.getAliasForJoinPath(path, { matchPopulateJoins: true });
|
|
202
|
-
/* v8 ignore next
|
|
233
|
+
/* v8 ignore next */
|
|
203
234
|
if (!relationAlias) {
|
|
204
235
|
return;
|
|
205
236
|
}
|
|
@@ -221,9 +252,13 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
221
252
|
}
|
|
222
253
|
return;
|
|
223
254
|
}
|
|
255
|
+
const mapToPk = !!(ref || prop.mapToPk);
|
|
256
|
+
const targetProps = mapToPk
|
|
257
|
+
? meta2.getPrimaryProps()
|
|
258
|
+
: meta2.props.filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []));
|
|
224
259
|
// If the primary key value for the relation is null, we know we haven't joined to anything
|
|
225
260
|
// and therefore we don't return any record (since all values would be null)
|
|
226
|
-
const hasPK = meta2.
|
|
261
|
+
const hasPK = meta2.getPrimaryProps().every(pk => pk.fieldNames.every(name => {
|
|
227
262
|
return root[`${relationAlias}__${name}`] != null;
|
|
228
263
|
}));
|
|
229
264
|
if (!hasPK) {
|
|
@@ -233,14 +268,18 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
233
268
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
234
269
|
result[prop.name] = null;
|
|
235
270
|
}
|
|
271
|
+
for (const prop of targetProps) {
|
|
272
|
+
for (const name of prop.fieldNames) {
|
|
273
|
+
delete root[`${relationAlias}__${name}`];
|
|
274
|
+
}
|
|
275
|
+
}
|
|
236
276
|
return;
|
|
237
277
|
}
|
|
238
278
|
let relationPojo = {};
|
|
239
279
|
meta2.props
|
|
240
280
|
.filter(prop => !ref && prop.persist === false && prop.fieldNames)
|
|
241
|
-
.filter(prop => !prop.lazy || populate.some(p => p.field === prop.name || p.all))
|
|
242
281
|
.forEach(prop => {
|
|
243
|
-
/* v8 ignore next
|
|
282
|
+
/* v8 ignore next */
|
|
244
283
|
if (prop.fieldNames.length > 1) { // composite keys
|
|
245
284
|
relationPojo[prop.name] = prop.fieldNames.map(name => root[`${relationAlias}__${name}`]);
|
|
246
285
|
}
|
|
@@ -249,10 +288,6 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
249
288
|
relationPojo[prop.name] = root[alias];
|
|
250
289
|
}
|
|
251
290
|
});
|
|
252
|
-
const mapToPk = !!(ref || prop.mapToPk);
|
|
253
|
-
const targetProps = mapToPk
|
|
254
|
-
? meta2.getPrimaryProps()
|
|
255
|
-
: meta2.props.filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []));
|
|
256
291
|
const tz = this.platform.getTimezone();
|
|
257
292
|
for (const prop of targetProps) {
|
|
258
293
|
if (prop.fieldNames.every(name => typeof root[`${relationAlias}__${name}`] === 'undefined')) {
|
|
@@ -293,7 +328,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
293
328
|
// properties can be mapped to multiple places, e.g. when sharing a column in multiple FKs,
|
|
294
329
|
// so we need to delete them after everything is mapped from given level
|
|
295
330
|
for (const prop of targetProps) {
|
|
296
|
-
|
|
331
|
+
for (const name of prop.fieldNames) {
|
|
332
|
+
delete root[`${relationAlias}__${name}`];
|
|
333
|
+
}
|
|
297
334
|
}
|
|
298
335
|
if (mapToPk) {
|
|
299
336
|
const tmp = Object.values(relationPojo);
|
|
@@ -312,15 +349,20 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
312
349
|
});
|
|
313
350
|
}
|
|
314
351
|
async count(entityName, where, options = {}) {
|
|
315
|
-
const meta = this.metadata.
|
|
316
|
-
if (meta
|
|
352
|
+
const meta = this.metadata.get(entityName);
|
|
353
|
+
if (meta.virtual) {
|
|
317
354
|
return this.countVirtual(entityName, where, options);
|
|
318
355
|
}
|
|
319
|
-
|
|
320
|
-
const
|
|
321
|
-
const
|
|
322
|
-
const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging)
|
|
323
|
-
|
|
356
|
+
options = { populate: [], ...options };
|
|
357
|
+
const populate = options.populate;
|
|
358
|
+
const joinedProps = this.joinedProps(meta, populate, options);
|
|
359
|
+
const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging);
|
|
360
|
+
const populateWhere = this.buildPopulateWhere(meta, joinedProps, options);
|
|
361
|
+
if (meta && !Utils.isEmpty(populate)) {
|
|
362
|
+
this.buildFields(meta, populate, joinedProps, qb, qb.alias, options);
|
|
363
|
+
}
|
|
364
|
+
qb.__populateWhere = options._populateWhere;
|
|
365
|
+
qb.indexHint(options.indexHint)
|
|
324
366
|
.comment(options.comments)
|
|
325
367
|
.hintComment(options.hintComments)
|
|
326
368
|
.groupBy(options.groupBy)
|
|
@@ -328,8 +370,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
328
370
|
.populate(populate, joinedProps.length > 0 ? populateWhere : undefined, joinedProps.length > 0 ? options.populateFilter : undefined)
|
|
329
371
|
.withSchema(this.getSchemaName(meta, options))
|
|
330
372
|
.where(where);
|
|
331
|
-
if (
|
|
332
|
-
|
|
373
|
+
if (options.em) {
|
|
374
|
+
await qb.applyJoinedFilters(options.em, options.filters);
|
|
333
375
|
}
|
|
334
376
|
return this.rethrow(qb.getCount());
|
|
335
377
|
}
|
|
@@ -338,7 +380,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
338
380
|
const meta = this.metadata.find(entityName);
|
|
339
381
|
const collections = this.extractManyToMany(entityName, data);
|
|
340
382
|
const pks = meta?.primaryKeys ?? [this.config.getNamingStrategy().referenceColumnName()];
|
|
341
|
-
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
|
|
383
|
+
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
|
|
342
384
|
const res = await this.rethrow(qb.insert(data).execute('run', false));
|
|
343
385
|
res.row = res.row || {};
|
|
344
386
|
let pk;
|
|
@@ -386,7 +428,12 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
386
428
|
sql += ' ' + data.map(() => `select null as ${this.platform.quoteIdentifier(pks[0])}`).join(' union all ');
|
|
387
429
|
}
|
|
388
430
|
const addParams = (prop, row) => {
|
|
389
|
-
|
|
431
|
+
const rowValue = row[prop.name];
|
|
432
|
+
if (prop.nullable && rowValue === null) {
|
|
433
|
+
params.push(null);
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
let value = rowValue ?? prop.default;
|
|
390
437
|
if (prop.kind === ReferenceKind.EMBEDDED && prop.object) {
|
|
391
438
|
if (prop.array && value) {
|
|
392
439
|
value = this.platform.cloneEmbeddable(value);
|
|
@@ -399,14 +446,14 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
399
446
|
value = this.mapDataToFieldNames(value, false, prop.embeddedProps, options.convertCustomTypes);
|
|
400
447
|
}
|
|
401
448
|
}
|
|
402
|
-
if (options.convertCustomTypes && prop.customType) {
|
|
403
|
-
params.push(prop.customType.convertToDatabaseValue(value, this.platform, { key: prop.name, mode: 'query-data' }));
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
449
|
if (typeof value === 'undefined' && this.platform.usesDefaultKeyword()) {
|
|
407
450
|
params.push(raw('default'));
|
|
408
451
|
return;
|
|
409
452
|
}
|
|
453
|
+
if (options.convertCustomTypes && prop.customType) {
|
|
454
|
+
params.push(prop.customType.convertToDatabaseValue(value, this.platform, { key: prop.name, mode: 'query-data' }));
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
410
457
|
params.push(value);
|
|
411
458
|
};
|
|
412
459
|
if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
|
|
@@ -465,9 +512,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
465
512
|
if (transform) {
|
|
466
513
|
sql = transform(sql);
|
|
467
514
|
}
|
|
468
|
-
const res = await this.execute(sql, params, 'run', options.ctx);
|
|
515
|
+
const res = await this.execute(sql, params, 'run', options.ctx, options.loggerContext);
|
|
469
516
|
let pk;
|
|
470
|
-
/* v8 ignore next
|
|
517
|
+
/* v8 ignore next */
|
|
471
518
|
if (pks.length > 1) { // owner has composite pk
|
|
472
519
|
pk = data.map(d => Utils.getPrimaryKeyCond(d, pks));
|
|
473
520
|
}
|
|
@@ -493,7 +540,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
493
540
|
where = { [meta?.primaryKeys[0] ?? pks[0]]: where };
|
|
494
541
|
}
|
|
495
542
|
if (Utils.hasObjectKeys(data)) {
|
|
496
|
-
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes)
|
|
543
|
+
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext)
|
|
497
544
|
.withSchema(this.getSchemaName(meta, options));
|
|
498
545
|
if (options.upsert) {
|
|
499
546
|
/* v8 ignore next */
|
|
@@ -532,7 +579,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
532
579
|
const meta = this.metadata.get(entityName);
|
|
533
580
|
if (options.upsert) {
|
|
534
581
|
const uniqueFields = options.onConflictFields ?? (Utils.isPlainObject(where[0]) ? Object.keys(where[0]).flatMap(key => Utils.splitPrimaryKeys(key)) : meta.primaryKeys);
|
|
535
|
-
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
|
|
582
|
+
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
|
|
536
583
|
const returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options);
|
|
537
584
|
qb.insert(data)
|
|
538
585
|
.onConflict(uniqueFields)
|
|
@@ -645,7 +692,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
645
692
|
/* v8 ignore next */
|
|
646
693
|
sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
|
|
647
694
|
}
|
|
648
|
-
const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx));
|
|
695
|
+
const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx, options.loggerContext));
|
|
649
696
|
for (let i = 0; i < collections.length; i++) {
|
|
650
697
|
await this.processManyToMany(meta, where[i], collections[i], false, options);
|
|
651
698
|
}
|
|
@@ -657,7 +704,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
657
704
|
if (Utils.isPrimaryKey(where) && pks.length === 1) {
|
|
658
705
|
where = { [pks[0]]: where };
|
|
659
706
|
}
|
|
660
|
-
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false).delete(where).withSchema(this.getSchemaName(meta, options));
|
|
707
|
+
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false, options.loggerContext).delete(where).withSchema(this.getSchemaName(meta, options));
|
|
661
708
|
return this.rethrow(qb.execute('run', false));
|
|
662
709
|
}
|
|
663
710
|
/**
|
|
@@ -721,7 +768,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
721
768
|
await this.rethrow(query.execute());
|
|
722
769
|
continue;
|
|
723
770
|
}
|
|
724
|
-
/* v8 ignore next
|
|
771
|
+
/* v8 ignore next */
|
|
725
772
|
const query = qb.update({ [coll.property.mappedBy]: pks })
|
|
726
773
|
.where({ [cols.join(Utils.PK_SEPARATOR)]: { $in: insertDiff } });
|
|
727
774
|
await this.rethrow(query.execute());
|
|
@@ -744,126 +791,96 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
744
791
|
schema = this.config.get('schema');
|
|
745
792
|
}
|
|
746
793
|
const tableName = `${schema ?? '_'}.${pivotMeta.tableName}`;
|
|
747
|
-
const persister = groups[tableName] ??= new PivotCollectionPersister(pivotMeta, this, options?.ctx, schema);
|
|
748
|
-
persister.enqueueUpdate(coll.property, insertDiff, deleteDiff, pks);
|
|
794
|
+
const persister = groups[tableName] ??= new PivotCollectionPersister(pivotMeta, this, options?.ctx, schema, options?.loggerContext);
|
|
795
|
+
persister.enqueueUpdate(coll.property, insertDiff, deleteDiff, pks, coll.isInitialized());
|
|
749
796
|
}
|
|
750
797
|
for (const persister of Utils.values(groups)) {
|
|
751
798
|
await this.rethrow(persister.execute());
|
|
752
799
|
}
|
|
753
800
|
}
|
|
754
801
|
async loadFromPivotTable(prop, owners, where = {}, orderBy, ctx, options, pivotJoin) {
|
|
802
|
+
if (owners.length === 0) {
|
|
803
|
+
return {};
|
|
804
|
+
}
|
|
755
805
|
const pivotMeta = this.metadata.find(prop.pivotEntity);
|
|
756
806
|
const pivotProp1 = pivotMeta.relations[prop.owner ? 1 : 0];
|
|
757
807
|
const pivotProp2 = pivotMeta.relations[prop.owner ? 0 : 1];
|
|
758
808
|
const ownerMeta = this.metadata.find(pivotProp2.type);
|
|
759
|
-
options = { ...options };
|
|
760
|
-
const qb = this.createQueryBuilder(prop.pivotEntity, ctx, options.connectionType, undefined, options?.logging)
|
|
761
|
-
.withSchema(this.getSchemaName(pivotMeta, options))
|
|
762
|
-
.indexHint(options.indexHint)
|
|
763
|
-
.comment(options.comments)
|
|
764
|
-
.hintComment(options.hintComments);
|
|
765
|
-
const pivotAlias = qb.alias;
|
|
766
|
-
const pivotKey = pivotProp2.joinColumns.map(column => `${pivotAlias}.${column}`).join(Utils.PK_SEPARATOR);
|
|
767
809
|
const cond = {
|
|
768
|
-
[
|
|
810
|
+
[pivotProp2.name]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) },
|
|
769
811
|
};
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
const
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
const
|
|
782
|
-
|
|
783
|
-
...
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
const additionalFields = [];
|
|
793
|
-
for (const field of targetFields) {
|
|
794
|
-
const f = field.toString();
|
|
795
|
-
additionalFields.push(f.includes('.') ? field : `${targetAlias}.${f}`);
|
|
796
|
-
if (RawQueryFragment.isKnownFragment(field)) {
|
|
797
|
-
qb.rawFragments.add(f);
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
fields.unshift(...additionalFields);
|
|
801
|
-
// we need to handle 1:1 owner auto-joins explicitly, as the QB type is the pivot table, not the target
|
|
802
|
-
populate.forEach(hint => {
|
|
803
|
-
const alias = qb.getNextAlias(prop.targetMeta.tableName);
|
|
804
|
-
qb.leftJoin(`${targetAlias}.${hint.field}`, alias);
|
|
805
|
-
// eslint-disable-next-line dot-notation
|
|
806
|
-
for (const join of Object.values(qb['_joins'])) {
|
|
807
|
-
const [propName] = hint.field.split(':', 2);
|
|
808
|
-
if (join.alias === alias && join.prop.name === propName) {
|
|
809
|
-
fields.push(...qb.helper.mapJoinColumns(qb.type, join));
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
});
|
|
813
|
-
}
|
|
814
|
-
qb.select(fields)
|
|
815
|
-
.where({ [pivotProp1.name]: where })
|
|
816
|
-
.orderBy(orderBy)
|
|
817
|
-
.setLockMode(options.lockMode, options.lockTableAliases);
|
|
818
|
-
if (owners.length === 1 && (options.offset != null || options.limit != null)) {
|
|
819
|
-
qb.limit(options.limit, options.offset);
|
|
820
|
-
}
|
|
821
|
-
const res = owners.length ? await this.rethrow(qb.execute('all', { mergeResults: false, mapResults: false })) : [];
|
|
822
|
-
const tmp = {};
|
|
823
|
-
const items = res.map((row) => {
|
|
824
|
-
const root = super.mapResult(row, prop.targetMeta);
|
|
825
|
-
this.mapJoinedProps(root, prop.targetMeta, populate, qb, root, tmp, pivotMeta.className + '.' + pivotProp1.name);
|
|
826
|
-
return root;
|
|
812
|
+
if (!Utils.isEmpty(where)) {
|
|
813
|
+
cond[pivotProp1.name] = { ...where };
|
|
814
|
+
}
|
|
815
|
+
where = cond;
|
|
816
|
+
const populateField = pivotJoin ? `${pivotProp1.name}:ref` : pivotProp1.name;
|
|
817
|
+
const populate = this.autoJoinOneToOneOwner(prop.targetMeta, options?.populate ?? [], options?.fields);
|
|
818
|
+
const childFields = !Utils.isEmpty(options?.fields) ? options.fields.map(f => `${pivotProp1.name}.${f}`) : [];
|
|
819
|
+
const childExclude = !Utils.isEmpty(options?.exclude) ? options.exclude.map(f => `${pivotProp1.name}.${f}`) : [];
|
|
820
|
+
const fields = pivotJoin
|
|
821
|
+
? [pivotProp1.name, pivotProp2.name]
|
|
822
|
+
: [pivotProp1.name, pivotProp2.name, ...childFields];
|
|
823
|
+
const res = await this.find(pivotMeta.className, where, {
|
|
824
|
+
ctx,
|
|
825
|
+
...options,
|
|
826
|
+
fields,
|
|
827
|
+
exclude: childExclude,
|
|
828
|
+
orderBy: this.getPivotOrderBy(prop, pivotProp1, orderBy, options?.orderBy),
|
|
829
|
+
populate: [{ field: populateField, strategy: LoadStrategy.JOINED, joinType: JoinType.innerJoin, children: populate }],
|
|
830
|
+
populateWhere: undefined,
|
|
831
|
+
// @ts-ignore
|
|
832
|
+
_populateWhere: 'infer',
|
|
833
|
+
populateFilter: !Utils.isEmpty(options?.populateFilter) ? { [pivotProp2.name]: options?.populateFilter } : undefined,
|
|
827
834
|
});
|
|
828
|
-
qb.clearRawFragmentsCache();
|
|
829
835
|
const map = {};
|
|
830
|
-
const pkProps = ownerMeta.getPrimaryProps();
|
|
831
836
|
for (const owner of owners) {
|
|
832
|
-
const key = Utils.getPrimaryKeyHash(
|
|
833
|
-
const pkProp = pkProps[idx];
|
|
834
|
-
return pkProp.customType ? pkProp.customType.convertToJSValue(owner[idx], this.platform) : owner[idx];
|
|
835
|
-
}));
|
|
837
|
+
const key = Utils.getPrimaryKeyHash(owner);
|
|
836
838
|
map[key] = [];
|
|
837
839
|
}
|
|
838
|
-
for (const item of
|
|
839
|
-
const key = Utils.getPrimaryKeyHash(
|
|
840
|
-
|
|
841
|
-
return pkProp.customType ? pkProp.customType.convertToJSValue(item[`fk__${col}`], this.platform) : item[`fk__${col}`];
|
|
842
|
-
}));
|
|
843
|
-
map[key].push(item);
|
|
844
|
-
prop.joinColumns.forEach(col => delete item[`fk__${col}`]);
|
|
845
|
-
prop.inverseJoinColumns.forEach((col, idx) => {
|
|
846
|
-
Utils.renameKey(item, `fk__${col}`, prop.targetMeta.primaryKeys[idx]);
|
|
847
|
-
});
|
|
840
|
+
for (const item of res) {
|
|
841
|
+
const key = Utils.getPrimaryKeyHash(Utils.asArray(item[pivotProp2.name]));
|
|
842
|
+
map[key].push(item[pivotProp1.name]);
|
|
848
843
|
}
|
|
849
844
|
return map;
|
|
850
845
|
}
|
|
851
|
-
getPivotOrderBy(prop, pivotProp,
|
|
852
|
-
// FIXME this is ignoring the rest of the array items
|
|
846
|
+
getPivotOrderBy(prop, pivotProp, orderBy, parentOrderBy) {
|
|
853
847
|
if (!Utils.isEmpty(orderBy)) {
|
|
854
|
-
return
|
|
848
|
+
return Utils.asArray(orderBy).map(o => ({ [pivotProp.name]: o }));
|
|
849
|
+
}
|
|
850
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY && Utils.asArray(parentOrderBy).some(o => o[prop.name])) {
|
|
851
|
+
return Utils.asArray(parentOrderBy)
|
|
852
|
+
.filter(o => o[prop.name])
|
|
853
|
+
.map(o => ({ [pivotProp.name]: o[prop.name] }));
|
|
855
854
|
}
|
|
856
855
|
if (!Utils.isEmpty(prop.orderBy)) {
|
|
857
|
-
return
|
|
856
|
+
return Utils.asArray(prop.orderBy).map(o => ({ [pivotProp.name]: o }));
|
|
858
857
|
}
|
|
859
858
|
if (prop.fixedOrder) {
|
|
860
|
-
return [{ [
|
|
859
|
+
return [{ [prop.fixedOrderColumn]: QueryOrder.ASC }];
|
|
861
860
|
}
|
|
862
861
|
return [];
|
|
863
862
|
}
|
|
864
863
|
async execute(query, params = [], method = 'all', ctx, loggerContext) {
|
|
865
864
|
return this.rethrow(this.connection.execute(query, params, method, ctx, loggerContext));
|
|
866
865
|
}
|
|
866
|
+
async *stream(entityName, where, options) {
|
|
867
|
+
options = { populate: [], orderBy: [], ...options };
|
|
868
|
+
const meta = this.metadata.find(entityName);
|
|
869
|
+
if (meta?.virtual) {
|
|
870
|
+
yield* this.streamFromVirtual(entityName, where, options);
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
const qb = await this.createQueryBuilderFromOptions(meta, where, options);
|
|
874
|
+
try {
|
|
875
|
+
const result = qb.stream(options);
|
|
876
|
+
for await (const item of result) {
|
|
877
|
+
yield item;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
catch (e) {
|
|
881
|
+
throw this.convertException(e);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
867
884
|
/**
|
|
868
885
|
* 1:1 owner side needs to be marked for population so QB auto-joins the owner id
|
|
869
886
|
*/
|
|
@@ -875,7 +892,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
875
892
|
const toPopulate = meta.relations
|
|
876
893
|
.filter(prop => prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner && !prop.lazy && !relationsToPopulate.includes(prop.name))
|
|
877
894
|
.filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
|
|
878
|
-
.map(prop => ({ field: `${prop.name}:ref`, strategy:
|
|
895
|
+
.map(prop => ({ field: `${prop.name}:ref`, strategy: LoadStrategy.JOINED }));
|
|
879
896
|
return [...populate, ...toPopulate];
|
|
880
897
|
}
|
|
881
898
|
/**
|
|
@@ -885,14 +902,15 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
885
902
|
return populate.filter(hint => {
|
|
886
903
|
const [propName, ref] = hint.field.split(':', 2);
|
|
887
904
|
const prop = meta.properties[propName] || {};
|
|
888
|
-
|
|
905
|
+
const strategy = getLoadingStrategy(hint.strategy || prop.strategy || options?.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
906
|
+
if (ref && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind)) {
|
|
889
907
|
return true;
|
|
890
908
|
}
|
|
891
909
|
// skip redundant joins for 1:1 owner population hints when using `mapToPk`
|
|
892
910
|
if (prop.kind === ReferenceKind.ONE_TO_ONE && prop.mapToPk && prop.owner) {
|
|
893
911
|
return false;
|
|
894
912
|
}
|
|
895
|
-
if (
|
|
913
|
+
if (strategy !== LoadStrategy.JOINED) {
|
|
896
914
|
// force joined strategy for explicit 1:1 owner populate hint as it would require a join anyway
|
|
897
915
|
return prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner;
|
|
898
916
|
}
|
|
@@ -903,31 +921,26 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
903
921
|
* @internal
|
|
904
922
|
*/
|
|
905
923
|
mergeJoinedResult(rawResults, meta, joinedProps) {
|
|
924
|
+
if (rawResults.length <= 1) {
|
|
925
|
+
return rawResults;
|
|
926
|
+
}
|
|
906
927
|
const res = [];
|
|
907
928
|
const map = {};
|
|
929
|
+
const collectionsToMerge = {};
|
|
930
|
+
const hints = joinedProps.map(hint => {
|
|
931
|
+
const [propName, ref] = hint.field.split(':', 2);
|
|
932
|
+
return { propName, ref, children: hint.children };
|
|
933
|
+
});
|
|
908
934
|
for (const item of rawResults) {
|
|
909
935
|
const pk = Utils.getCompositeKeyHash(item, meta);
|
|
910
936
|
if (map[pk]) {
|
|
911
|
-
for (const
|
|
912
|
-
const [propName, ref] = hint.field.split(':', 2);
|
|
913
|
-
const prop = meta.properties[propName];
|
|
937
|
+
for (const { propName } of hints) {
|
|
914
938
|
if (!item[propName]) {
|
|
915
939
|
continue;
|
|
916
940
|
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
}
|
|
921
|
-
switch (prop.kind) {
|
|
922
|
-
case ReferenceKind.ONE_TO_MANY:
|
|
923
|
-
case ReferenceKind.MANY_TO_MANY:
|
|
924
|
-
map[pk][propName] = this.mergeJoinedResult([...map[pk][propName], ...item[propName]], prop.targetMeta, hint.children ?? []);
|
|
925
|
-
break;
|
|
926
|
-
case ReferenceKind.MANY_TO_ONE:
|
|
927
|
-
case ReferenceKind.ONE_TO_ONE:
|
|
928
|
-
map[pk][propName] = this.mergeJoinedResult([map[pk][propName], item[propName]], prop.targetMeta, hint.children ?? [])[0];
|
|
929
|
-
break;
|
|
930
|
-
}
|
|
941
|
+
collectionsToMerge[pk] ??= {};
|
|
942
|
+
collectionsToMerge[pk][propName] ??= [map[pk][propName]];
|
|
943
|
+
collectionsToMerge[pk][propName].push(item[propName]);
|
|
931
944
|
}
|
|
932
945
|
}
|
|
933
946
|
else {
|
|
@@ -935,37 +948,59 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
935
948
|
res.push(item);
|
|
936
949
|
}
|
|
937
950
|
}
|
|
951
|
+
for (const pk in collectionsToMerge) {
|
|
952
|
+
const entity = map[pk];
|
|
953
|
+
const collections = collectionsToMerge[pk];
|
|
954
|
+
for (const { propName, ref, children } of hints) {
|
|
955
|
+
if (!collections[propName]) {
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
958
|
+
const prop = meta.properties[propName];
|
|
959
|
+
const items = collections[propName].flat();
|
|
960
|
+
if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind) && ref) {
|
|
961
|
+
entity[propName] = items;
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
switch (prop.kind) {
|
|
965
|
+
case ReferenceKind.ONE_TO_MANY:
|
|
966
|
+
case ReferenceKind.MANY_TO_MANY:
|
|
967
|
+
entity[propName] = this.mergeJoinedResult(items, prop.targetMeta, children ?? []);
|
|
968
|
+
break;
|
|
969
|
+
case ReferenceKind.MANY_TO_ONE:
|
|
970
|
+
case ReferenceKind.ONE_TO_ONE:
|
|
971
|
+
entity[propName] = this.mergeJoinedResult(items, prop.targetMeta, children ?? [])[0];
|
|
972
|
+
break;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
}
|
|
938
976
|
return res;
|
|
939
977
|
}
|
|
978
|
+
shouldHaveColumn(meta, prop, populate, fields, exclude) {
|
|
979
|
+
if (!this.platform.shouldHaveColumn(prop, populate, exclude)) {
|
|
980
|
+
return false;
|
|
981
|
+
}
|
|
982
|
+
if (!fields || fields.includes('*') || prop.primary || meta.root.discriminatorColumn === prop.name) {
|
|
983
|
+
return true;
|
|
984
|
+
}
|
|
985
|
+
return fields.some(f => f === prop.name || f.toString().startsWith(prop.name + '.'));
|
|
986
|
+
}
|
|
940
987
|
getFieldsForJoinedLoad(qb, meta, options) {
|
|
941
988
|
const fields = [];
|
|
942
989
|
const populate = options.populate ?? [];
|
|
943
990
|
const joinedProps = this.joinedProps(meta, populate, options);
|
|
944
|
-
const shouldHaveColumn = (meta, prop, populate, fields) => {
|
|
945
|
-
if (!this.platform.shouldHaveColumn(prop, populate, options.exclude)) {
|
|
946
|
-
return false;
|
|
947
|
-
}
|
|
948
|
-
if (!fields || fields.includes('*') || prop.primary || meta.root.discriminatorColumn === prop.name) {
|
|
949
|
-
return true;
|
|
950
|
-
}
|
|
951
|
-
return fields.some(f => f === prop.name || f.toString().startsWith(prop.name + '.'));
|
|
952
|
-
};
|
|
953
991
|
const populateWhereAll = options?._populateWhere === 'all' || Utils.isEmpty(options?._populateWhere);
|
|
954
992
|
// root entity is already handled, skip that
|
|
955
993
|
if (options.parentJoinPath) {
|
|
956
994
|
// alias all fields in the primary table
|
|
957
995
|
meta.props
|
|
958
|
-
.filter(prop => shouldHaveColumn(meta, prop, populate, options.explicitFields))
|
|
959
|
-
.forEach(prop => fields.push(...this.mapPropToFieldNames(qb, prop, options.parentTableAlias)));
|
|
996
|
+
.filter(prop => this.shouldHaveColumn(meta, prop, populate, options.explicitFields, options.exclude))
|
|
997
|
+
.forEach(prop => fields.push(...this.mapPropToFieldNames(qb, prop, options.parentTableAlias, options.explicitFields)));
|
|
960
998
|
}
|
|
961
999
|
for (const hint of joinedProps) {
|
|
962
1000
|
const [propName, ref] = hint.field.split(':', 2);
|
|
963
1001
|
const prop = meta.properties[propName];
|
|
964
1002
|
// ignore ref joins of known FKs unless it's a filter hint
|
|
965
|
-
if (ref && !hint.filter && (prop.kind === ReferenceKind.MANY_TO_ONE || (prop.kind === ReferenceKind.ONE_TO_ONE &&
|
|
966
|
-
continue;
|
|
967
|
-
}
|
|
968
|
-
if (options.count && !options?.populateFilter?.[prop.name]) {
|
|
1003
|
+
if (ref && !hint.filter && (prop.kind === ReferenceKind.MANY_TO_ONE || (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner))) {
|
|
969
1004
|
continue;
|
|
970
1005
|
}
|
|
971
1006
|
const meta2 = this.metadata.find(prop.type);
|
|
@@ -976,11 +1011,14 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
976
1011
|
if (!options.parentJoinPath && populateWhereAll && !hint.filter && !path.startsWith('[populate]')) {
|
|
977
1012
|
path = '[populate]' + path;
|
|
978
1013
|
}
|
|
1014
|
+
const mandatoryToOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && !prop.nullable;
|
|
979
1015
|
const joinType = pivotRefJoin
|
|
980
1016
|
? JoinType.pivotJoin
|
|
981
|
-
: hint.
|
|
982
|
-
?
|
|
983
|
-
:
|
|
1017
|
+
: hint.joinType
|
|
1018
|
+
? hint.joinType
|
|
1019
|
+
: (hint.filter && !prop.nullable) || mandatoryToOneProperty
|
|
1020
|
+
? JoinType.innerJoin
|
|
1021
|
+
: JoinType.leftJoin;
|
|
984
1022
|
qb.join(field, tableAlias, {}, joinType, path);
|
|
985
1023
|
if (pivotRefJoin) {
|
|
986
1024
|
fields.push(...prop.joinColumns.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)), ...prop.inverseJoinColumns.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)));
|
|
@@ -1012,7 +1050,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1012
1050
|
parentJoinPath: path,
|
|
1013
1051
|
}));
|
|
1014
1052
|
}
|
|
1015
|
-
else if (hint.filter || prop.mapToPk) {
|
|
1053
|
+
else if (hint.filter || prop.mapToPk || (ref && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind))) {
|
|
1016
1054
|
fields.push(...prop.referencedColumnNames.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)));
|
|
1017
1055
|
}
|
|
1018
1056
|
}
|
|
@@ -1021,9 +1059,18 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1021
1059
|
/**
|
|
1022
1060
|
* @internal
|
|
1023
1061
|
*/
|
|
1024
|
-
mapPropToFieldNames(qb, prop, tableAlias) {
|
|
1062
|
+
mapPropToFieldNames(qb, prop, tableAlias, explicitFields) {
|
|
1063
|
+
if (prop.kind === ReferenceKind.EMBEDDED && !prop.object) {
|
|
1064
|
+
return Object.entries(prop.embeddedProps).flatMap(([name, childProp]) => {
|
|
1065
|
+
const childFields = explicitFields ? Utils.extractChildElements(explicitFields, prop.name) : [];
|
|
1066
|
+
if (!this.shouldHaveColumn(prop.targetMeta, { ...childProp, name }, [], childFields.length > 0 ? childFields : undefined)) {
|
|
1067
|
+
return [];
|
|
1068
|
+
}
|
|
1069
|
+
return this.mapPropToFieldNames(qb, childProp, tableAlias, childFields);
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1025
1072
|
const aliased = this.platform.quoteIdentifier(`${tableAlias}__${prop.fieldNames[0]}`);
|
|
1026
|
-
if (prop.customTypes?.some(type => type?.convertToJSValueSQL)) {
|
|
1073
|
+
if (prop.customTypes?.some(type => !!type?.convertToJSValueSQL)) {
|
|
1027
1074
|
return prop.fieldNames.map((col, idx) => {
|
|
1028
1075
|
if (!prop.customTypes[idx]?.convertToJSValueSQL) {
|
|
1029
1076
|
return col;
|
|
@@ -1087,7 +1134,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1087
1134
|
for (const prop of meta.relations) {
|
|
1088
1135
|
if (collections[prop.name]) {
|
|
1089
1136
|
const pivotMeta = this.metadata.find(prop.pivotEntity);
|
|
1090
|
-
const persister = new PivotCollectionPersister(pivotMeta, this, options?.ctx, options?.schema);
|
|
1137
|
+
const persister = new PivotCollectionPersister(pivotMeta, this, options?.ctx, options?.schema, options?.loggerContext);
|
|
1091
1138
|
persister.enqueueUpdate(prop, collections[prop.name], clear, pks);
|
|
1092
1139
|
await this.rethrow(persister.execute());
|
|
1093
1140
|
}
|
|
@@ -1154,7 +1201,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1154
1201
|
let path = parentPath;
|
|
1155
1202
|
const meta2 = this.metadata.find(prop.type);
|
|
1156
1203
|
const childOrder = orderHint[prop.name];
|
|
1157
|
-
if (![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || !prop.owner || Utils.isPlainObject(childOrder)) {
|
|
1204
|
+
if (prop.kind !== ReferenceKind.SCALAR && (![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || !prop.owner || Utils.isPlainObject(childOrder))) {
|
|
1158
1205
|
path += `.${propName}`;
|
|
1159
1206
|
}
|
|
1160
1207
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && typeof childOrder !== 'object') {
|
|
@@ -1162,10 +1209,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1162
1209
|
}
|
|
1163
1210
|
const join = qb.getJoinForPath(path, { matchPopulateJoins: true });
|
|
1164
1211
|
const propAlias = qb.getAliasForJoinPath(join ?? path, { matchPopulateJoins: true }) ?? parentAlias;
|
|
1165
|
-
if (!join
|
|
1212
|
+
if (!join) {
|
|
1166
1213
|
continue;
|
|
1167
1214
|
}
|
|
1168
|
-
if (![ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind) && typeof childOrder === 'object') {
|
|
1215
|
+
if (join && ![ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind) && typeof childOrder === 'object') {
|
|
1169
1216
|
const children = this.buildPopulateOrderBy(qb, meta2, Utils.asArray(childOrder), path, explicit, propAlias);
|
|
1170
1217
|
orderBy.push(...children);
|
|
1171
1218
|
continue;
|
|
@@ -1243,7 +1290,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1243
1290
|
}
|
|
1244
1291
|
return ret;
|
|
1245
1292
|
}
|
|
1246
|
-
processField(meta, prop, field, ret
|
|
1293
|
+
processField(meta, prop, field, ret) {
|
|
1247
1294
|
if (!prop || (prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner)) {
|
|
1248
1295
|
return;
|
|
1249
1296
|
}
|
|
@@ -1256,7 +1303,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1256
1303
|
const top = parts.shift();
|
|
1257
1304
|
for (const key of Object.keys(prop.embeddedProps)) {
|
|
1258
1305
|
if (!top || key === top) {
|
|
1259
|
-
this.processField(meta, prop.embeddedProps[key], parts.join('.'), ret
|
|
1306
|
+
this.processField(meta, prop.embeddedProps[key], parts.join('.'), ret);
|
|
1260
1307
|
}
|
|
1261
1308
|
}
|
|
1262
1309
|
return;
|
|
@@ -1266,16 +1313,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1266
1313
|
}
|
|
1267
1314
|
ret.push(prop.name);
|
|
1268
1315
|
}
|
|
1269
|
-
|
|
1270
|
-
if (hint.field === prop.name || hint.field === name || hint.all) {
|
|
1271
|
-
return true;
|
|
1272
|
-
}
|
|
1273
|
-
if (prop.embedded && hint.children && meta.properties[prop.embedded[0]].name === hint.field) {
|
|
1274
|
-
return hint.children.some(c => this.isPopulated(meta, prop, c, prop.embedded[1]));
|
|
1275
|
-
}
|
|
1276
|
-
return false;
|
|
1277
|
-
}
|
|
1278
|
-
buildFields(meta, populate, joinedProps, qb, alias, options, count = false) {
|
|
1316
|
+
buildFields(meta, populate, joinedProps, qb, alias, options) {
|
|
1279
1317
|
const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => this.isPopulated(meta, prop, p)));
|
|
1280
1318
|
const hasLazyFormulas = meta.props.some(p => p.lazy && p.formula);
|
|
1281
1319
|
const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL && p.persist !== false);
|
|
@@ -1298,7 +1336,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1298
1336
|
where: {},
|
|
1299
1337
|
aliasMap: qb.getAliasMap(),
|
|
1300
1338
|
});
|
|
1301
|
-
this.processField(meta, prop, parts.join('.'), ret
|
|
1339
|
+
this.processField(meta, prop, parts.join('.'), ret);
|
|
1302
1340
|
}
|
|
1303
1341
|
if (!options.fields.includes('*') && !options.fields.includes(`${qb.alias}.*`)) {
|
|
1304
1342
|
ret.unshift(...meta.primaryKeys.filter(pk => !options.fields.includes(pk)));
|
|
@@ -1308,7 +1346,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1308
1346
|
}
|
|
1309
1347
|
}
|
|
1310
1348
|
else if (!Utils.isEmpty(options.exclude) || lazyProps.some(p => !p.formula && (p.kind !== '1:1' || p.owner))) {
|
|
1311
|
-
const props = meta.props.filter(prop => this.platform.shouldHaveColumn(prop, populate, options.exclude, false));
|
|
1349
|
+
const props = meta.props.filter(prop => this.platform.shouldHaveColumn(prop, populate, options.exclude, false, false));
|
|
1312
1350
|
ret.push(...props.filter(p => !lazyProps.includes(p)).map(p => p.name));
|
|
1313
1351
|
addFormulas = true;
|
|
1314
1352
|
}
|
|
@@ -1320,16 +1358,19 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1320
1358
|
ret.push('*');
|
|
1321
1359
|
}
|
|
1322
1360
|
if (ret.length > 0 && !hasExplicitFields && addFormulas) {
|
|
1323
|
-
meta.props
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1361
|
+
for (const prop of meta.props) {
|
|
1362
|
+
if (lazyProps.includes(prop)) {
|
|
1363
|
+
continue;
|
|
1364
|
+
}
|
|
1365
|
+
if (prop.formula) {
|
|
1366
|
+
const a = this.platform.quoteIdentifier(alias);
|
|
1367
|
+
const aliased = this.platform.quoteIdentifier(prop.fieldNames[0]);
|
|
1368
|
+
ret.push(raw(`${prop.formula(a)} as ${aliased}`));
|
|
1369
|
+
}
|
|
1370
|
+
if (!prop.object && (prop.hasConvertToDatabaseValueSQL || prop.hasConvertToJSValueSQL)) {
|
|
1371
|
+
ret.push(prop.name);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1333
1374
|
}
|
|
1334
1375
|
// add joined relations after the root entity fields
|
|
1335
1376
|
if (joinedProps.length > 0) {
|
|
@@ -1338,7 +1379,6 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1338
1379
|
exclude: options.exclude,
|
|
1339
1380
|
populate,
|
|
1340
1381
|
parentTableAlias: alias,
|
|
1341
|
-
count,
|
|
1342
1382
|
...options,
|
|
1343
1383
|
}));
|
|
1344
1384
|
}
|