@mikro-orm/knex 6.0.0-dev.9 → 6.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 +5 -2
- package/AbstractSqlConnection.js +25 -17
- package/AbstractSqlDriver.d.ts +19 -18
- package/AbstractSqlDriver.js +171 -70
- package/AbstractSqlPlatform.d.ts +1 -0
- package/AbstractSqlPlatform.js +26 -5
- package/MonkeyPatchable.d.ts +1 -0
- package/MonkeyPatchable.js +3 -0
- package/README.md +64 -107
- package/SqlEntityManager.d.ts +2 -5
- package/SqlEntityManager.js +5 -9
- package/SqlEntityRepository.d.ts +6 -4
- package/SqlEntityRepository.js +10 -8
- package/index.mjs +20 -6
- package/package.json +7 -7
- package/query/ArrayCriteriaNode.d.ts +3 -3
- package/query/CriteriaNode.d.ts +8 -8
- package/query/CriteriaNode.js +9 -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 +27 -16
- package/query/QueryBuilder.js +250 -91
- package/query/QueryBuilderHelper.d.ts +6 -9
- package/query/QueryBuilderHelper.js +112 -96
- package/query/ScalarCriteriaNode.d.ts +3 -2
- package/query/ScalarCriteriaNode.js +9 -6
- package/query/enums.js +1 -1
- package/schema/DatabaseSchema.d.ts +4 -1
- package/schema/DatabaseSchema.js +22 -6
- package/schema/DatabaseTable.d.ts +2 -1
- package/schema/DatabaseTable.js +25 -24
- package/schema/SchemaComparator.js +9 -2
- package/schema/SchemaHelper.d.ts +5 -3
- package/schema/SchemaHelper.js +10 -4
- package/schema/SqlSchemaGenerator.d.ts +3 -5
- package/schema/SqlSchemaGenerator.js +41 -20
- package/typings.d.ts +10 -7
package/AbstractSqlDriver.js
CHANGED
|
@@ -26,19 +26,28 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
26
26
|
}
|
|
27
27
|
const populate = this.autoJoinOneToOneOwner(meta, options.populate, options.fields);
|
|
28
28
|
const joinedProps = this.joinedProps(meta, populate);
|
|
29
|
-
const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false);
|
|
29
|
+
const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging);
|
|
30
30
|
const fields = this.buildFields(meta, populate, joinedProps, qb, options.fields);
|
|
31
31
|
const joinedPropsOrderBy = this.buildJoinedPropsOrderBy(entityName, qb, meta, joinedProps);
|
|
32
|
+
const orderBy = [...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy];
|
|
32
33
|
if (core_1.Utils.isPrimaryKey(where, meta.compositePK)) {
|
|
33
34
|
where = { [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
|
|
34
35
|
}
|
|
36
|
+
const { first, last, before, after } = options;
|
|
37
|
+
const isCursorPagination = [first, last, before, after].some(v => v != null);
|
|
35
38
|
qb.select(fields)
|
|
36
39
|
.populate(populate, joinedProps.length > 0 ? options.populateWhere : undefined)
|
|
37
40
|
.where(where)
|
|
38
|
-
.orderBy([...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy])
|
|
39
41
|
.groupBy(options.groupBy)
|
|
40
42
|
.having(options.having)
|
|
41
43
|
.withSchema(this.getSchemaName(meta, options));
|
|
44
|
+
if (isCursorPagination) {
|
|
45
|
+
const { orderBy: newOrderBy, where } = this.processCursorOptions(meta, options, orderBy);
|
|
46
|
+
qb.andWhere(where).orderBy(newOrderBy);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
qb.orderBy(orderBy);
|
|
50
|
+
}
|
|
42
51
|
if (options.limit !== undefined) {
|
|
43
52
|
qb.limit(options.limit, options.offset);
|
|
44
53
|
}
|
|
@@ -47,6 +56,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
47
56
|
}
|
|
48
57
|
core_1.Utils.asArray(options.flags).forEach(flag => qb.setFlag(flag));
|
|
49
58
|
const result = await this.rethrow(qb.execute('all'));
|
|
59
|
+
if (isCursorPagination && !first && !!last) {
|
|
60
|
+
result.reverse();
|
|
61
|
+
}
|
|
50
62
|
if (joinedProps.length > 0) {
|
|
51
63
|
return this.mergeJoinedResult(result, meta);
|
|
52
64
|
}
|
|
@@ -61,7 +73,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
61
73
|
opts.limit = 1;
|
|
62
74
|
}
|
|
63
75
|
if (opts.limit > 0 && !opts.flags?.includes(core_1.QueryFlag.DISABLE_PAGINATE)) {
|
|
64
|
-
opts.flags
|
|
76
|
+
opts.flags ??= [];
|
|
65
77
|
opts.flags.push(core_1.QueryFlag.DISABLE_PAGINATE);
|
|
66
78
|
}
|
|
67
79
|
const res = await this.find(entityName, where, opts);
|
|
@@ -79,7 +91,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
79
91
|
const em = this.createEntityManager(false);
|
|
80
92
|
em.setTransactionContext(options.ctx);
|
|
81
93
|
const res = meta.expression(em, where, options);
|
|
82
|
-
if (res instanceof
|
|
94
|
+
if (res instanceof query_1.QueryBuilder) {
|
|
83
95
|
return this.wrapVirtualExpressionInSubquery(meta, res.getFormattedQuery(), where, options);
|
|
84
96
|
}
|
|
85
97
|
/* istanbul ignore next */
|
|
@@ -97,7 +109,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
97
109
|
const em = this.createEntityManager(false);
|
|
98
110
|
em.setTransactionContext(options.ctx);
|
|
99
111
|
const res = meta.expression(em, where, options);
|
|
100
|
-
if (res instanceof
|
|
112
|
+
if (res instanceof query_1.QueryBuilder) {
|
|
101
113
|
return this.wrapVirtualExpressionInSubquery(meta, res.getFormattedQuery(), where, options, query_1.QueryType.COUNT);
|
|
102
114
|
}
|
|
103
115
|
/* istanbul ignore next */
|
|
@@ -112,7 +124,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
112
124
|
qb.where(where);
|
|
113
125
|
const kqb = qb.getKnexQuery().clear('select');
|
|
114
126
|
if (type === query_1.QueryType.COUNT) {
|
|
115
|
-
kqb.select(
|
|
127
|
+
kqb.select(this.connection.getKnex().raw('count(*) as count'));
|
|
116
128
|
}
|
|
117
129
|
else { // select
|
|
118
130
|
kqb.select('*');
|
|
@@ -154,11 +166,12 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
154
166
|
}));
|
|
155
167
|
if (!hasPK) {
|
|
156
168
|
// initialize empty collections
|
|
157
|
-
if ([core_1.
|
|
158
|
-
result[relation.name]
|
|
169
|
+
if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
|
|
170
|
+
result[relation.name] ??= [];
|
|
159
171
|
}
|
|
160
172
|
return;
|
|
161
173
|
}
|
|
174
|
+
const tz = this.platform.getTimezone();
|
|
162
175
|
meta2.props
|
|
163
176
|
.filter(prop => this.platform.shouldHaveColumn(prop, p.children || []))
|
|
164
177
|
.forEach(prop => {
|
|
@@ -166,6 +179,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
166
179
|
relationPojo[prop.name] = prop.fieldNames.map(name => root[`${relationAlias}__${name}`]);
|
|
167
180
|
prop.fieldNames.map(name => delete root[`${relationAlias}__${name}`]);
|
|
168
181
|
}
|
|
182
|
+
else if (prop.runtimeType === 'Date') {
|
|
183
|
+
const alias = `${relationAlias}__${prop.fieldNames[0]}`;
|
|
184
|
+
relationPojo[prop.name] = (typeof root[alias] === 'string' ? new Date(root[alias]) : root[alias]);
|
|
185
|
+
delete root[alias];
|
|
186
|
+
}
|
|
169
187
|
else {
|
|
170
188
|
const alias = `${relationAlias}__${prop.fieldNames[0]}`;
|
|
171
189
|
relationPojo[prop.name] = root[alias];
|
|
@@ -179,8 +197,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
179
197
|
else {
|
|
180
198
|
map[key] = result;
|
|
181
199
|
}
|
|
182
|
-
if ([core_1.
|
|
183
|
-
result[relation.name]
|
|
200
|
+
if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
|
|
201
|
+
result[relation.name] ??= [];
|
|
184
202
|
this.appendToCollection(meta2, result[relation.name], relationPojo);
|
|
185
203
|
}
|
|
186
204
|
else {
|
|
@@ -215,7 +233,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
215
233
|
return this.rethrow(qb.getCount());
|
|
216
234
|
}
|
|
217
235
|
async nativeInsert(entityName, data, options = {}) {
|
|
218
|
-
options.convertCustomTypes
|
|
236
|
+
options.convertCustomTypes ??= true;
|
|
219
237
|
const meta = this.metadata.find(entityName);
|
|
220
238
|
const collections = this.extractManyToMany(entityName, data);
|
|
221
239
|
const pks = meta?.primaryKeys ?? [this.config.getNamingStrategy().referenceColumnName()];
|
|
@@ -235,16 +253,20 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
235
253
|
return res;
|
|
236
254
|
}
|
|
237
255
|
async nativeInsertMany(entityName, data, options = {}) {
|
|
238
|
-
options.processCollections
|
|
239
|
-
options.convertCustomTypes
|
|
256
|
+
options.processCollections ??= true;
|
|
257
|
+
options.convertCustomTypes ??= true;
|
|
240
258
|
const meta = this.metadata.find(entityName);
|
|
241
259
|
const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
|
|
242
260
|
const pks = this.getPrimaryKeyFields(entityName);
|
|
243
261
|
const set = new Set();
|
|
244
|
-
data.forEach(row =>
|
|
262
|
+
data.forEach(row => core_1.Utils.keys(row).forEach(k => set.add(k)));
|
|
245
263
|
const props = [...set].map(name => meta?.properties[name] ?? { name, fieldNames: [name] });
|
|
246
|
-
|
|
264
|
+
let fields = core_1.Utils.flatten(props.map(prop => prop.fieldNames));
|
|
265
|
+
const duplicates = core_1.Utils.findDuplicates(fields);
|
|
247
266
|
const params = [];
|
|
267
|
+
if (duplicates.length) {
|
|
268
|
+
fields = core_1.Utils.unique(fields);
|
|
269
|
+
}
|
|
248
270
|
/* istanbul ignore next */
|
|
249
271
|
const tableName = meta ? this.getTableName(meta, options) : this.platform.quoteIdentifier(entityName);
|
|
250
272
|
let sql = `insert into ${tableName} `;
|
|
@@ -255,29 +277,45 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
255
277
|
else {
|
|
256
278
|
sql += ' ' + data.map(() => `select null as ${this.platform.quoteIdentifier(pks[0])}`).join(' union all ');
|
|
257
279
|
}
|
|
280
|
+
const addParams = (prop, row) => {
|
|
281
|
+
if (options.convertCustomTypes && prop.customType) {
|
|
282
|
+
params.push(prop.customType.convertToDatabaseValue(row[prop.name], this.platform, { key: prop.name, mode: 'query-data' }));
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
params.push(row[prop.name]);
|
|
286
|
+
};
|
|
258
287
|
if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
|
|
259
288
|
sql += data.map(row => {
|
|
260
289
|
const keys = [];
|
|
261
290
|
props.forEach(prop => {
|
|
262
291
|
if (prop.fieldNames.length > 1) {
|
|
263
|
-
|
|
264
|
-
|
|
292
|
+
const param = row[prop.name] ?? prop.fieldNames.map(() => null);
|
|
293
|
+
const key = (row[prop.name] ?? prop.fieldNames).map(() => '?');
|
|
294
|
+
prop.fieldNames.forEach((field, idx) => {
|
|
295
|
+
if (duplicates.includes(field)) {
|
|
296
|
+
param.splice(idx, 1);
|
|
297
|
+
key.splice(idx, 1);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
params.push(...param);
|
|
301
|
+
keys.push(...key);
|
|
265
302
|
}
|
|
266
303
|
else if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(row[prop.name])) {
|
|
267
304
|
keys.push(prop.customType.convertToDatabaseValueSQL('?', this.platform));
|
|
268
|
-
|
|
305
|
+
addParams(prop, row);
|
|
269
306
|
}
|
|
270
307
|
else {
|
|
271
|
-
|
|
308
|
+
addParams(prop, row);
|
|
272
309
|
keys.push('?');
|
|
273
310
|
}
|
|
274
311
|
});
|
|
275
312
|
return '(' + (keys.join(', ') || 'default') + ')';
|
|
276
313
|
}).join(', ');
|
|
277
314
|
}
|
|
278
|
-
if (this.platform.usesReturningStatement()) {
|
|
279
|
-
|
|
280
|
-
|
|
315
|
+
if (meta && this.platform.usesReturningStatement()) {
|
|
316
|
+
const returningProps = meta.props
|
|
317
|
+
.filter(prop => prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw))
|
|
318
|
+
.filter(prop => !(prop.name in data[0]) || core_1.Utils.isRawSql(data[0][prop.name]));
|
|
281
319
|
const returningFields = core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames));
|
|
282
320
|
/* istanbul ignore next */
|
|
283
321
|
sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
|
|
@@ -289,8 +327,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
289
327
|
pk = data.map(d => core_1.Utils.getPrimaryKeyCond(d, pks));
|
|
290
328
|
}
|
|
291
329
|
else {
|
|
292
|
-
res.row
|
|
293
|
-
res.rows
|
|
330
|
+
res.row ??= {};
|
|
331
|
+
res.rows ??= [];
|
|
294
332
|
pk = data.map((d, i) => d[pks[0]] ?? res.rows[i]?.[pks[0]]).map(d => [d]);
|
|
295
333
|
res.insertId = res.insertId || res.row[pks[0]];
|
|
296
334
|
}
|
|
@@ -300,7 +338,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
300
338
|
return res;
|
|
301
339
|
}
|
|
302
340
|
async nativeUpdate(entityName, where, data, options = {}) {
|
|
303
|
-
options.convertCustomTypes
|
|
341
|
+
options.convertCustomTypes ??= true;
|
|
304
342
|
const meta = this.metadata.find(entityName);
|
|
305
343
|
const pks = this.getPrimaryKeyFields(entityName);
|
|
306
344
|
const collections = this.extractManyToMany(entityName, data);
|
|
@@ -314,11 +352,12 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
314
352
|
.withSchema(this.getSchemaName(meta, options));
|
|
315
353
|
if (options.upsert) {
|
|
316
354
|
/* istanbul ignore next */
|
|
317
|
-
const uniqueFields = core_1.Utils.isPlainObject(where) ?
|
|
355
|
+
const uniqueFields = core_1.Utils.isPlainObject(where) ? core_1.Utils.keys(where) : meta.primaryKeys;
|
|
318
356
|
/* istanbul ignore next */
|
|
319
357
|
qb.insert(data)
|
|
320
358
|
.onConflict(uniqueFields.map(p => meta?.properties[p]?.fieldNames[0] ?? p))
|
|
321
|
-
.merge(
|
|
359
|
+
.merge(core_1.Utils.keys(data).filter(f => !uniqueFields.includes(f)))
|
|
360
|
+
.returning(meta?.comparableProps.filter(p => !p.lazy && !p.embeddable && !(p.name in data)).map(p => p.name) ?? '*');
|
|
322
361
|
}
|
|
323
362
|
else {
|
|
324
363
|
qb.update(data).where(where);
|
|
@@ -331,12 +370,29 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
331
370
|
return res;
|
|
332
371
|
}
|
|
333
372
|
async nativeUpdateMany(entityName, where, data, options = {}) {
|
|
334
|
-
options.processCollections
|
|
335
|
-
options.convertCustomTypes
|
|
373
|
+
options.processCollections ??= true;
|
|
374
|
+
options.convertCustomTypes ??= true;
|
|
336
375
|
const meta = this.metadata.get(entityName);
|
|
376
|
+
if (options.upsert) {
|
|
377
|
+
const uniqueFields = core_1.Utils.isPlainObject(where[0]) ? Object.keys(where[0]) : meta.primaryKeys;
|
|
378
|
+
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
|
|
379
|
+
qb.insert(data)
|
|
380
|
+
.onConflict(uniqueFields.map(p => meta.properties[p]?.fieldNames[0] ?? p))
|
|
381
|
+
.merge(Object.keys(data[0]).filter(f => !uniqueFields.includes(f)))
|
|
382
|
+
.returning(meta.comparableProps.filter(p => !p.lazy && !p.embeddable && !(p.name in data[0])).map(p => p.name) ?? '*');
|
|
383
|
+
return qb.execute('run', false);
|
|
384
|
+
}
|
|
337
385
|
const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
|
|
338
386
|
const keys = new Set();
|
|
339
|
-
|
|
387
|
+
const returning = new Set();
|
|
388
|
+
data.forEach(row => {
|
|
389
|
+
core_1.Utils.keys(row).forEach(k => {
|
|
390
|
+
keys.add(k);
|
|
391
|
+
if (core_1.Utils.isRawSql(row[k])) {
|
|
392
|
+
returning.add(k);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
});
|
|
340
396
|
const pkCond = core_1.Utils.flatten(meta.primaryKeys.map(pk => meta.properties[pk].fieldNames)).map(pk => `${this.platform.quoteIdentifier(pk)} = ?`).join(' and ');
|
|
341
397
|
const params = [];
|
|
342
398
|
let sql = `update ${this.getTableName(meta, options)} set `;
|
|
@@ -365,7 +421,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
365
421
|
const versionProperty = meta.properties[meta.versionProperty];
|
|
366
422
|
const quotedFieldName = this.platform.quoteIdentifier(versionProperty.fieldNames[0]);
|
|
367
423
|
sql += `${quotedFieldName} = `;
|
|
368
|
-
if (versionProperty.
|
|
424
|
+
if (versionProperty.runtimeType === 'Date') {
|
|
369
425
|
sql += this.platform.getCurrentTimestampSQL(versionProperty.length);
|
|
370
426
|
}
|
|
371
427
|
else {
|
|
@@ -396,6 +452,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
396
452
|
return '?';
|
|
397
453
|
});
|
|
398
454
|
sql += ` in (${conds.join(', ')})`;
|
|
455
|
+
if (this.platform.usesReturningStatement() && returning.size > 0) {
|
|
456
|
+
const returningFields = core_1.Utils.flatten([...returning].map(prop => meta.properties[prop].fieldNames));
|
|
457
|
+
/* istanbul ignore next */
|
|
458
|
+
sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
|
|
459
|
+
}
|
|
399
460
|
const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx));
|
|
400
461
|
for (let i = 0; i < collections.length; i++) {
|
|
401
462
|
await this.processManyToMany(meta, where[i], collections[i], false, options);
|
|
@@ -430,7 +491,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
430
491
|
deleteDiff.push(...snapshot);
|
|
431
492
|
insertDiff.push(...current);
|
|
432
493
|
}
|
|
433
|
-
if (coll.property.
|
|
494
|
+
if (coll.property.kind === core_1.ReferenceKind.ONE_TO_MANY) {
|
|
434
495
|
const cols = coll.property.referencedColumnNames;
|
|
435
496
|
const qb = this.createQueryBuilder(coll.property.type, ctx, 'write')
|
|
436
497
|
.withSchema(this.getSchemaName(meta, options))
|
|
@@ -444,16 +505,22 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
444
505
|
const pivotMeta = this.metadata.find(coll.property.pivotEntity);
|
|
445
506
|
if (pivotMeta.schema === '*') {
|
|
446
507
|
/* istanbul ignore next */
|
|
447
|
-
options
|
|
508
|
+
options ??= {};
|
|
448
509
|
options.schema = ownerSchema;
|
|
449
510
|
}
|
|
450
511
|
return this.rethrow(this.updateCollectionDiff(meta, coll.property, pks, deleteDiff, insertDiff, options));
|
|
451
512
|
}
|
|
452
513
|
async loadFromPivotTable(prop, owners, where = {}, orderBy, ctx, options) {
|
|
453
514
|
const pivotProp2 = this.getPivotInverseProperty(prop);
|
|
515
|
+
const prop2 = prop.targetMeta.properties[prop.mappedBy ?? prop.inversedBy];
|
|
454
516
|
const ownerMeta = this.metadata.find(pivotProp2.type);
|
|
455
517
|
const pivotMeta = this.metadata.find(prop.pivotEntity);
|
|
456
|
-
const
|
|
518
|
+
const qb = this.createQueryBuilder(prop.type, ctx, options?.connectionType).withSchema(this.getSchemaName(prop.targetMeta, options));
|
|
519
|
+
const pivotAlias = qb.getNextAlias(pivotMeta.tableName);
|
|
520
|
+
const pivotKey = pivotProp2.joinColumns.map(column => `${pivotAlias}.${column}`).join(core_1.Utils.PK_SEPARATOR);
|
|
521
|
+
const cond = {
|
|
522
|
+
[pivotKey]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) },
|
|
523
|
+
};
|
|
457
524
|
/* istanbul ignore if */
|
|
458
525
|
if (!core_1.Utils.isEmpty(where) && Object.keys(where).every(k => core_1.Utils.isOperator(k, false))) {
|
|
459
526
|
where = cond;
|
|
@@ -461,22 +528,32 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
461
528
|
else {
|
|
462
529
|
where = { ...where, ...cond };
|
|
463
530
|
}
|
|
464
|
-
orderBy = this.getPivotOrderBy(prop, orderBy);
|
|
465
|
-
const
|
|
466
|
-
.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES)
|
|
467
|
-
.withSchema(this.getSchemaName(prop.targetMeta, options));
|
|
468
|
-
const populate = this.autoJoinOneToOneOwner(prop.targetMeta, [{ field: prop.pivotEntity }]);
|
|
531
|
+
orderBy = this.getPivotOrderBy(prop, pivotAlias, orderBy);
|
|
532
|
+
const populate = this.autoJoinOneToOneOwner(prop.targetMeta, []);
|
|
469
533
|
const fields = this.buildFields(prop.targetMeta, (options?.populate ?? []), [], qb, options?.fields);
|
|
470
|
-
|
|
534
|
+
const k1 = !prop.owner ? 'joinColumns' : 'inverseJoinColumns';
|
|
535
|
+
const k2 = prop.owner ? 'joinColumns' : 'inverseJoinColumns';
|
|
536
|
+
const cols = [
|
|
537
|
+
...prop[k1].map(col => `${pivotAlias}.${col} as fk__${col}`),
|
|
538
|
+
...prop[k2].map(col => `${pivotAlias}.${col} as fk__${col}`),
|
|
539
|
+
];
|
|
540
|
+
fields.push(...cols);
|
|
541
|
+
qb.select(fields)
|
|
542
|
+
.join(prop2.name, pivotAlias, {}, 'pivotJoin', undefined, options?.schema)
|
|
543
|
+
.populate(populate)
|
|
544
|
+
.where(where)
|
|
545
|
+
.orderBy(orderBy)
|
|
546
|
+
.setLockMode(options?.lockMode, options?.lockTableAliases);
|
|
471
547
|
if (owners.length === 1 && (options?.offset != null || options?.limit != null)) {
|
|
472
548
|
qb.limit(options.limit, options.offset);
|
|
473
549
|
}
|
|
550
|
+
const schema = this.config.get('schema');
|
|
474
551
|
if (prop.targetMeta.schema !== '*' && pivotMeta.schema === '*' && options?.schema) {
|
|
475
552
|
// eslint-disable-next-line dot-notation
|
|
476
553
|
qb['finalize']();
|
|
477
554
|
// eslint-disable-next-line dot-notation
|
|
478
555
|
Object.values(qb['_joins']).forEach(join => {
|
|
479
|
-
join.schema =
|
|
556
|
+
join.schema = schema;
|
|
480
557
|
});
|
|
481
558
|
}
|
|
482
559
|
const items = owners.length ? await this.rethrow(qb.execute('all')) : [];
|
|
@@ -500,6 +577,18 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
500
577
|
});
|
|
501
578
|
return map;
|
|
502
579
|
}
|
|
580
|
+
getPivotOrderBy(prop, pivotAlias, orderBy) {
|
|
581
|
+
if (!core_1.Utils.isEmpty(orderBy)) {
|
|
582
|
+
return orderBy;
|
|
583
|
+
}
|
|
584
|
+
if (!core_1.Utils.isEmpty(prop.orderBy)) {
|
|
585
|
+
return core_1.Utils.asArray(prop.orderBy);
|
|
586
|
+
}
|
|
587
|
+
if (prop.fixedOrder) {
|
|
588
|
+
return [{ [`${pivotAlias}.${prop.fixedOrderColumn}`]: core_1.QueryOrder.ASC }];
|
|
589
|
+
}
|
|
590
|
+
return [];
|
|
591
|
+
}
|
|
503
592
|
async execute(queryOrKnex, params = [], method = 'all', ctx) {
|
|
504
593
|
return this.rethrow(this.connection.execute(queryOrKnex, params, method, ctx));
|
|
505
594
|
}
|
|
@@ -512,7 +601,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
512
601
|
}
|
|
513
602
|
const relationsToPopulate = populate.map(({ field }) => field);
|
|
514
603
|
const toPopulate = meta.relations
|
|
515
|
-
.filter(prop => prop.
|
|
604
|
+
.filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
|
|
516
605
|
.filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
|
|
517
606
|
.map(prop => ({ field: prop.name, strategy: prop.strategy }));
|
|
518
607
|
return [...populate, ...toPopulate];
|
|
@@ -520,7 +609,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
520
609
|
joinedProps(meta, populate) {
|
|
521
610
|
return populate.filter(p => {
|
|
522
611
|
const prop = meta.properties[p.field] || {};
|
|
523
|
-
return (p.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && prop.
|
|
612
|
+
return (p.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && prop.kind !== core_1.ReferenceKind.SCALAR;
|
|
524
613
|
});
|
|
525
614
|
}
|
|
526
615
|
/**
|
|
@@ -545,14 +634,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
545
634
|
getFieldsForJoinedLoad(qb, meta, explicitFields, populate = [], parentTableAlias, parentJoinPath) {
|
|
546
635
|
const fields = [];
|
|
547
636
|
const joinedProps = this.joinedProps(meta, populate);
|
|
637
|
+
if (explicitFields?.includes('*')) {
|
|
638
|
+
fields.push('*');
|
|
639
|
+
}
|
|
548
640
|
const shouldHaveColumn = (prop, populate, fields) => {
|
|
549
641
|
if (!this.platform.shouldHaveColumn(prop, populate)) {
|
|
550
642
|
return false;
|
|
551
643
|
}
|
|
552
644
|
if (!fields || prop.primary) {
|
|
553
|
-
return
|
|
645
|
+
return !fields?.includes('*');
|
|
554
646
|
}
|
|
555
|
-
return fields.
|
|
647
|
+
return fields.some(f => f === prop.name || f.toString().startsWith(prop.name + '.'));
|
|
556
648
|
};
|
|
557
649
|
// alias all fields in the primary table
|
|
558
650
|
meta.props
|
|
@@ -579,24 +671,25 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
579
671
|
* @internal
|
|
580
672
|
*/
|
|
581
673
|
mapPropToFieldNames(qb, prop, tableAlias) {
|
|
582
|
-
const
|
|
674
|
+
const knex = this.connection.getKnex();
|
|
675
|
+
const aliased = knex.ref(tableAlias ? `${tableAlias}__${prop.fieldNames[0]}` : prop.fieldNames[0]).toString();
|
|
583
676
|
if (prop.customType?.convertToJSValueSQL && tableAlias) {
|
|
584
|
-
const prefixed =
|
|
585
|
-
return [
|
|
677
|
+
const prefixed = knex.ref(prop.fieldNames[0]).withSchema(tableAlias).toString();
|
|
678
|
+
return [(0, core_1.raw)(`${prop.customType.convertToJSValueSQL(prefixed, this.platform)} as ${aliased}`)];
|
|
586
679
|
}
|
|
587
680
|
if (prop.formula) {
|
|
588
|
-
const alias =
|
|
589
|
-
return [`${prop.formula(alias)} as ${aliased}`];
|
|
681
|
+
const alias = knex.ref(tableAlias ?? qb.alias).toString();
|
|
682
|
+
return [(0, core_1.raw)(`${prop.formula(alias)} as ${aliased}`)];
|
|
590
683
|
}
|
|
591
684
|
if (tableAlias) {
|
|
592
|
-
return prop.fieldNames.map(fieldName =>
|
|
685
|
+
return prop.fieldNames.map(fieldName => knex.ref(fieldName).withSchema(tableAlias).as(`${tableAlias}__${fieldName}`));
|
|
593
686
|
}
|
|
594
687
|
return prop.fieldNames;
|
|
595
688
|
}
|
|
596
689
|
/** @internal */
|
|
597
|
-
createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes) {
|
|
690
|
+
createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes, logging) {
|
|
598
691
|
const connectionType = this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
|
|
599
|
-
const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType);
|
|
692
|
+
const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType, undefined, logging);
|
|
600
693
|
if (!convertCustomTypes) {
|
|
601
694
|
qb.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
|
|
602
695
|
}
|
|
@@ -620,7 +713,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
620
713
|
}
|
|
621
714
|
const ret = {};
|
|
622
715
|
this.metadata.find(entityName).relations.forEach(prop => {
|
|
623
|
-
if (prop.
|
|
716
|
+
if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && data[prop.name]) {
|
|
624
717
|
ret[prop.name] = data[prop.name].map((item) => core_1.Utils.asArray(item));
|
|
625
718
|
delete data[prop.name];
|
|
626
719
|
}
|
|
@@ -642,9 +735,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
642
735
|
deleteDiff = [];
|
|
643
736
|
}
|
|
644
737
|
if (deleteDiff === true || deleteDiff.length > 0) {
|
|
645
|
-
const qb1 = this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
|
|
646
|
-
.withSchema(this.getSchemaName(meta, options))
|
|
647
|
-
.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
|
|
738
|
+
const qb1 = this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write').withSchema(this.getSchemaName(meta, options));
|
|
648
739
|
const knex = qb1.getKnex();
|
|
649
740
|
if (Array.isArray(deleteDiff)) {
|
|
650
741
|
knex.whereIn(prop.inverseJoinColumns, deleteDiff);
|
|
@@ -663,12 +754,15 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
663
754
|
});
|
|
664
755
|
/* istanbul ignore else */
|
|
665
756
|
if (this.platform.allowsMultiInsert()) {
|
|
666
|
-
await this.nativeInsertMany(prop.pivotEntity, items, {
|
|
757
|
+
await this.nativeInsertMany(prop.pivotEntity, items, {
|
|
758
|
+
...options,
|
|
759
|
+
convertCustomTypes: false,
|
|
760
|
+
processCollections: false,
|
|
761
|
+
});
|
|
667
762
|
}
|
|
668
763
|
else {
|
|
669
764
|
await core_1.Utils.runSerial(items, item => {
|
|
670
765
|
return this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
|
|
671
|
-
.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES)
|
|
672
766
|
.withSchema(this.getSchemaName(meta, options))
|
|
673
767
|
.insert(item)
|
|
674
768
|
.execute('run', false);
|
|
@@ -677,9 +771,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
677
771
|
}
|
|
678
772
|
async lockPessimistic(entity, options) {
|
|
679
773
|
const meta = (0, core_1.helper)(entity).__meta;
|
|
680
|
-
const qb = this.createQueryBuilder(entity.constructor.name, options.ctx).
|
|
774
|
+
const qb = this.createQueryBuilder(entity.constructor.name, options.ctx).withSchema(options.schema ?? meta.schema);
|
|
681
775
|
const cond = core_1.Utils.getPrimaryKeyCond(entity, meta.primaryKeys);
|
|
682
|
-
qb.select('1').where(cond).setLockMode(options.lockMode, options.lockTableAliases);
|
|
776
|
+
qb.select((0, core_1.raw)('1')).where(cond).setLockMode(options.lockMode, options.lockTableAliases);
|
|
683
777
|
await this.rethrow(qb.execute());
|
|
684
778
|
}
|
|
685
779
|
buildJoinedPropsOrderBy(entityName, qb, meta, populate, parentPath) {
|
|
@@ -691,7 +785,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
691
785
|
const path = `${parentPath ? parentPath : entityName}.${relation.field}`;
|
|
692
786
|
const propAlias = qb.getAliasForJoinPath(path);
|
|
693
787
|
if (propOrderBy) {
|
|
694
|
-
|
|
788
|
+
core_1.Utils.keys(propOrderBy).forEach(field => {
|
|
695
789
|
orderBy.push({ [`${propAlias}.${field}`]: propOrderBy[field] });
|
|
696
790
|
});
|
|
697
791
|
}
|
|
@@ -718,10 +812,10 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
718
812
|
return ret;
|
|
719
813
|
}
|
|
720
814
|
processField(meta, prop, field, ret, populate, joinedProps, qb) {
|
|
721
|
-
if (!prop || (prop.
|
|
815
|
+
if (!prop || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner)) {
|
|
722
816
|
return;
|
|
723
817
|
}
|
|
724
|
-
if (prop.
|
|
818
|
+
if (prop.kind === core_1.ReferenceKind.EMBEDDED) {
|
|
725
819
|
if (prop.object) {
|
|
726
820
|
ret.push(prop.fieldNames[0]);
|
|
727
821
|
return;
|
|
@@ -735,8 +829,12 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
735
829
|
}
|
|
736
830
|
return;
|
|
737
831
|
}
|
|
832
|
+
if (prop.formula) {
|
|
833
|
+
ret.push(prop.name);
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
738
836
|
if (prop.fieldNames) {
|
|
739
|
-
ret.push(prop.fieldNames
|
|
837
|
+
ret.push(...prop.fieldNames);
|
|
740
838
|
}
|
|
741
839
|
}
|
|
742
840
|
buildFields(meta, populate, joinedProps, qb, fields) {
|
|
@@ -745,6 +843,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
745
843
|
const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL);
|
|
746
844
|
const hasExplicitFields = !!fields;
|
|
747
845
|
const ret = [];
|
|
846
|
+
let addFormulas = false;
|
|
748
847
|
if (joinedProps.length > 0) {
|
|
749
848
|
ret.push(...this.getFieldsForJoinedLoad(qb, meta, fields, populate));
|
|
750
849
|
}
|
|
@@ -770,20 +869,22 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
770
869
|
else if (lazyProps.filter(p => !p.formula).length > 0) {
|
|
771
870
|
const props = meta.props.filter(prop => this.platform.shouldHaveColumn(prop, populate, false));
|
|
772
871
|
ret.push(...core_1.Utils.flatten(props.filter(p => !lazyProps.includes(p)).map(p => p.fieldNames)));
|
|
872
|
+
addFormulas = true;
|
|
773
873
|
}
|
|
774
874
|
else if (hasLazyFormulas || requiresSQLConversion) {
|
|
775
875
|
ret.push('*');
|
|
876
|
+
addFormulas = true;
|
|
776
877
|
}
|
|
777
|
-
if (ret.length > 0 && !hasExplicitFields) {
|
|
878
|
+
if (ret.length > 0 && !hasExplicitFields && addFormulas) {
|
|
778
879
|
meta.props
|
|
779
880
|
.filter(prop => prop.formula && !lazyProps.includes(prop))
|
|
780
881
|
.forEach(prop => {
|
|
781
|
-
const alias =
|
|
782
|
-
const aliased =
|
|
783
|
-
ret.push(`${prop.formula(alias)} as ${aliased}`);
|
|
882
|
+
const alias = this.connection.getKnex().ref(qb.alias).toString();
|
|
883
|
+
const aliased = this.connection.getKnex().ref(prop.fieldNames[0]).toString();
|
|
884
|
+
ret.push((0, core_1.raw)(`${prop.formula(alias)} as ${aliased}`));
|
|
784
885
|
});
|
|
785
886
|
meta.props
|
|
786
|
-
.filter(prop => prop.
|
|
887
|
+
.filter(prop => prop.hasConvertToDatabaseValueSQL || prop.hasConvertToJSValueSQL)
|
|
787
888
|
.forEach(prop => ret.push(prop.name));
|
|
788
889
|
}
|
|
789
890
|
return ret.length > 0 ? core_1.Utils.unique(ret) : ['*'];
|
package/AbstractSqlPlatform.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare abstract class AbstractSqlPlatform extends Platform {
|
|
|
14
14
|
quoteValue(value: any): string;
|
|
15
15
|
formatQuery(sql: string, params: readonly any[]): string;
|
|
16
16
|
getSearchJsonPropertySQL(path: string, type: string, aliased: boolean): string;
|
|
17
|
+
getSearchJsonPropertyKey(path: string[], type: string, aliased: boolean): string;
|
|
17
18
|
isRaw(value: any): boolean;
|
|
18
19
|
supportsSchemas(): boolean;
|
|
19
20
|
/** @inheritDoc */
|
package/AbstractSqlPlatform.js
CHANGED
|
@@ -27,6 +27,9 @@ class AbstractSqlPlatform extends core_1.Platform {
|
|
|
27
27
|
return new schema_1.SqlSchemaGenerator(em ?? driver);
|
|
28
28
|
}
|
|
29
29
|
quoteValue(value) {
|
|
30
|
+
if (core_1.Utils.isRawSql(value)) {
|
|
31
|
+
return this.formatQuery(value.sql, value.params ?? []);
|
|
32
|
+
}
|
|
30
33
|
if (this.isRaw(value)) {
|
|
31
34
|
return value;
|
|
32
35
|
}
|
|
@@ -45,22 +48,32 @@ class AbstractSqlPlatform extends core_1.Platform {
|
|
|
45
48
|
let j = 0;
|
|
46
49
|
let pos = 0;
|
|
47
50
|
let ret = '';
|
|
51
|
+
if (sql[0] === '?') {
|
|
52
|
+
if (sql[1] === '?') {
|
|
53
|
+
ret += this.quoteIdentifier(params[j++]);
|
|
54
|
+
pos = 2;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
ret += this.quoteValue(params[j++]);
|
|
58
|
+
pos = 1;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
48
61
|
while (pos < sql.length) {
|
|
49
62
|
const idx = sql.indexOf('?', pos + 1);
|
|
50
63
|
if (idx === -1) {
|
|
51
64
|
ret += sql.substring(pos, sql.length);
|
|
52
65
|
break;
|
|
53
66
|
}
|
|
54
|
-
if (sql.
|
|
55
|
-
ret += sql.
|
|
67
|
+
if (sql.substring(idx - 1, idx + 1) === '\\?') {
|
|
68
|
+
ret += sql.substring(pos, idx - 1) + '?';
|
|
56
69
|
pos = idx + 1;
|
|
57
70
|
}
|
|
58
|
-
else if (sql.
|
|
59
|
-
ret += sql.
|
|
71
|
+
else if (sql.substring(idx, idx + 2) === '??') {
|
|
72
|
+
ret += sql.substring(pos, idx) + this.quoteIdentifier(params[j++]);
|
|
60
73
|
pos = idx + 2;
|
|
61
74
|
}
|
|
62
75
|
else {
|
|
63
|
-
ret += sql.
|
|
76
|
+
ret += sql.substring(pos, idx) + this.quoteValue(params[j++]);
|
|
64
77
|
pos = idx + 1;
|
|
65
78
|
}
|
|
66
79
|
}
|
|
@@ -69,6 +82,14 @@ class AbstractSqlPlatform extends core_1.Platform {
|
|
|
69
82
|
getSearchJsonPropertySQL(path, type, aliased) {
|
|
70
83
|
return this.getSearchJsonPropertyKey(path.split('->'), type, aliased);
|
|
71
84
|
}
|
|
85
|
+
getSearchJsonPropertyKey(path, type, aliased) {
|
|
86
|
+
const [a, ...b] = path;
|
|
87
|
+
const quoteKey = (key) => key.match(/^[a-z]\w*$/i) ? key : `"${key}"`;
|
|
88
|
+
if (aliased) {
|
|
89
|
+
return (0, core_1.raw)(alias => `json_extract(${this.quoteIdentifier(`${alias}.${a}`)}, '$.${b.map(quoteKey).join('.')}')`);
|
|
90
|
+
}
|
|
91
|
+
return (0, core_1.raw)(`json_extract(${this.quoteIdentifier(a)}, '$.${b.map(quoteKey).join('.')}')`);
|
|
92
|
+
}
|
|
72
93
|
isRaw(value) {
|
|
73
94
|
return super.isRaw(value) || (typeof value === 'object' && value !== null && value.client && ['Ref', 'Raw'].includes(value.constructor.name));
|
|
74
95
|
}
|
package/MonkeyPatchable.d.ts
CHANGED