@mikro-orm/knex 6.0.0-dev.8 → 6.0.0-dev.81
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 +18 -18
- package/AbstractSqlDriver.js +124 -49
- package/AbstractSqlPlatform.d.ts +1 -0
- package/AbstractSqlPlatform.js +20 -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 +19 -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 +25 -7
- package/query/QueryBuilder.js +237 -61
- package/query/QueryBuilderHelper.d.ts +3 -5
- package/query/QueryBuilderHelper.js +97 -75
- package/query/ScalarCriteriaNode.d.ts +3 -2
- package/query/ScalarCriteriaNode.js +9 -6
- 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 +8 -5
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);
|
|
@@ -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.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.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,7 +505,7 @@ 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));
|
|
@@ -512,7 +573,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
512
573
|
}
|
|
513
574
|
const relationsToPopulate = populate.map(({ field }) => field);
|
|
514
575
|
const toPopulate = meta.relations
|
|
515
|
-
.filter(prop => prop.
|
|
576
|
+
.filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
|
|
516
577
|
.filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
|
|
517
578
|
.map(prop => ({ field: prop.name, strategy: prop.strategy }));
|
|
518
579
|
return [...populate, ...toPopulate];
|
|
@@ -520,7 +581,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
520
581
|
joinedProps(meta, populate) {
|
|
521
582
|
return populate.filter(p => {
|
|
522
583
|
const prop = meta.properties[p.field] || {};
|
|
523
|
-
return (p.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && prop.
|
|
584
|
+
return (p.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && prop.kind !== core_1.ReferenceKind.SCALAR;
|
|
524
585
|
});
|
|
525
586
|
}
|
|
526
587
|
/**
|
|
@@ -545,14 +606,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
545
606
|
getFieldsForJoinedLoad(qb, meta, explicitFields, populate = [], parentTableAlias, parentJoinPath) {
|
|
546
607
|
const fields = [];
|
|
547
608
|
const joinedProps = this.joinedProps(meta, populate);
|
|
609
|
+
if (explicitFields?.includes('*')) {
|
|
610
|
+
fields.push('*');
|
|
611
|
+
}
|
|
548
612
|
const shouldHaveColumn = (prop, populate, fields) => {
|
|
549
613
|
if (!this.platform.shouldHaveColumn(prop, populate)) {
|
|
550
614
|
return false;
|
|
551
615
|
}
|
|
552
616
|
if (!fields || prop.primary) {
|
|
553
|
-
return
|
|
617
|
+
return !fields?.includes('*');
|
|
554
618
|
}
|
|
555
|
-
return fields.
|
|
619
|
+
return fields.some(f => f === prop.name || f.toString().startsWith(prop.name + '.'));
|
|
556
620
|
};
|
|
557
621
|
// alias all fields in the primary table
|
|
558
622
|
meta.props
|
|
@@ -582,11 +646,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
582
646
|
const aliased = qb.ref(tableAlias ? `${tableAlias}__${prop.fieldNames[0]}` : prop.fieldNames[0]).toString();
|
|
583
647
|
if (prop.customType?.convertToJSValueSQL && tableAlias) {
|
|
584
648
|
const prefixed = qb.ref(prop.fieldNames[0]).withSchema(tableAlias).toString();
|
|
585
|
-
return [
|
|
649
|
+
return [(0, core_1.raw)(`${prop.customType.convertToJSValueSQL(prefixed, this.platform)} as ${aliased}`)];
|
|
586
650
|
}
|
|
587
651
|
if (prop.formula) {
|
|
588
652
|
const alias = qb.ref(tableAlias ?? qb.alias).toString();
|
|
589
|
-
return [`${prop.formula(alias)} as ${aliased}`];
|
|
653
|
+
return [(0, core_1.raw)(`${prop.formula(alias)} as ${aliased}`)];
|
|
590
654
|
}
|
|
591
655
|
if (tableAlias) {
|
|
592
656
|
return prop.fieldNames.map(fieldName => qb.ref(fieldName).withSchema(tableAlias).as(`${tableAlias}__${fieldName}`));
|
|
@@ -594,9 +658,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
594
658
|
return prop.fieldNames;
|
|
595
659
|
}
|
|
596
660
|
/** @internal */
|
|
597
|
-
createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes) {
|
|
661
|
+
createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes, logging) {
|
|
598
662
|
const connectionType = this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
|
|
599
|
-
const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType);
|
|
663
|
+
const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType, undefined, logging);
|
|
600
664
|
if (!convertCustomTypes) {
|
|
601
665
|
qb.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
|
|
602
666
|
}
|
|
@@ -620,7 +684,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
620
684
|
}
|
|
621
685
|
const ret = {};
|
|
622
686
|
this.metadata.find(entityName).relations.forEach(prop => {
|
|
623
|
-
if (prop.
|
|
687
|
+
if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && data[prop.name]) {
|
|
624
688
|
ret[prop.name] = data[prop.name].map((item) => core_1.Utils.asArray(item));
|
|
625
689
|
delete data[prop.name];
|
|
626
690
|
}
|
|
@@ -663,7 +727,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
663
727
|
});
|
|
664
728
|
/* istanbul ignore else */
|
|
665
729
|
if (this.platform.allowsMultiInsert()) {
|
|
666
|
-
await this.nativeInsertMany(prop.pivotEntity, items, {
|
|
730
|
+
await this.nativeInsertMany(prop.pivotEntity, items, {
|
|
731
|
+
...options,
|
|
732
|
+
convertCustomTypes: false,
|
|
733
|
+
processCollections: false,
|
|
734
|
+
});
|
|
667
735
|
}
|
|
668
736
|
else {
|
|
669
737
|
await core_1.Utils.runSerial(items, item => {
|
|
@@ -679,7 +747,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
679
747
|
const meta = (0, core_1.helper)(entity).__meta;
|
|
680
748
|
const qb = this.createQueryBuilder(entity.constructor.name, options.ctx).unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES).withSchema(options.schema ?? meta.schema);
|
|
681
749
|
const cond = core_1.Utils.getPrimaryKeyCond(entity, meta.primaryKeys);
|
|
682
|
-
qb.select('1').where(cond).setLockMode(options.lockMode, options.lockTableAliases);
|
|
750
|
+
qb.select((0, core_1.raw)('1')).where(cond).setLockMode(options.lockMode, options.lockTableAliases);
|
|
683
751
|
await this.rethrow(qb.execute());
|
|
684
752
|
}
|
|
685
753
|
buildJoinedPropsOrderBy(entityName, qb, meta, populate, parentPath) {
|
|
@@ -691,7 +759,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
691
759
|
const path = `${parentPath ? parentPath : entityName}.${relation.field}`;
|
|
692
760
|
const propAlias = qb.getAliasForJoinPath(path);
|
|
693
761
|
if (propOrderBy) {
|
|
694
|
-
|
|
762
|
+
core_1.Utils.keys(propOrderBy).forEach(field => {
|
|
695
763
|
orderBy.push({ [`${propAlias}.${field}`]: propOrderBy[field] });
|
|
696
764
|
});
|
|
697
765
|
}
|
|
@@ -718,10 +786,10 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
718
786
|
return ret;
|
|
719
787
|
}
|
|
720
788
|
processField(meta, prop, field, ret, populate, joinedProps, qb) {
|
|
721
|
-
if (!prop || (prop.
|
|
789
|
+
if (!prop || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner)) {
|
|
722
790
|
return;
|
|
723
791
|
}
|
|
724
|
-
if (prop.
|
|
792
|
+
if (prop.kind === core_1.ReferenceKind.EMBEDDED) {
|
|
725
793
|
if (prop.object) {
|
|
726
794
|
ret.push(prop.fieldNames[0]);
|
|
727
795
|
return;
|
|
@@ -735,8 +803,12 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
735
803
|
}
|
|
736
804
|
return;
|
|
737
805
|
}
|
|
806
|
+
if (prop.formula) {
|
|
807
|
+
ret.push(prop.name);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
738
810
|
if (prop.fieldNames) {
|
|
739
|
-
ret.push(prop.fieldNames
|
|
811
|
+
ret.push(...prop.fieldNames);
|
|
740
812
|
}
|
|
741
813
|
}
|
|
742
814
|
buildFields(meta, populate, joinedProps, qb, fields) {
|
|
@@ -745,6 +817,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
745
817
|
const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL);
|
|
746
818
|
const hasExplicitFields = !!fields;
|
|
747
819
|
const ret = [];
|
|
820
|
+
let addFormulas = false;
|
|
748
821
|
if (joinedProps.length > 0) {
|
|
749
822
|
ret.push(...this.getFieldsForJoinedLoad(qb, meta, fields, populate));
|
|
750
823
|
}
|
|
@@ -770,20 +843,22 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
770
843
|
else if (lazyProps.filter(p => !p.formula).length > 0) {
|
|
771
844
|
const props = meta.props.filter(prop => this.platform.shouldHaveColumn(prop, populate, false));
|
|
772
845
|
ret.push(...core_1.Utils.flatten(props.filter(p => !lazyProps.includes(p)).map(p => p.fieldNames)));
|
|
846
|
+
addFormulas = true;
|
|
773
847
|
}
|
|
774
848
|
else if (hasLazyFormulas || requiresSQLConversion) {
|
|
775
849
|
ret.push('*');
|
|
850
|
+
addFormulas = true;
|
|
776
851
|
}
|
|
777
|
-
if (ret.length > 0 && !hasExplicitFields) {
|
|
852
|
+
if (ret.length > 0 && !hasExplicitFields && addFormulas) {
|
|
778
853
|
meta.props
|
|
779
854
|
.filter(prop => prop.formula && !lazyProps.includes(prop))
|
|
780
855
|
.forEach(prop => {
|
|
781
856
|
const alias = qb.ref(qb.alias).toString();
|
|
782
857
|
const aliased = qb.ref(prop.fieldNames[0]).toString();
|
|
783
|
-
ret.push(`${prop.formula(alias)} as ${aliased}`);
|
|
858
|
+
ret.push((0, core_1.raw)(`${prop.formula(alias)} as ${aliased}`));
|
|
784
859
|
});
|
|
785
860
|
meta.props
|
|
786
|
-
.filter(prop => prop.
|
|
861
|
+
.filter(prop => prop.hasConvertToDatabaseValueSQL || prop.hasConvertToJSValueSQL)
|
|
787
862
|
.forEach(prop => ret.push(prop.name));
|
|
788
863
|
}
|
|
789
864
|
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,26 @@ class AbstractSqlPlatform extends core_1.Platform {
|
|
|
45
48
|
let j = 0;
|
|
46
49
|
let pos = 0;
|
|
47
50
|
let ret = '';
|
|
51
|
+
if (sql[0] === '?' && sql[1] !== '?') {
|
|
52
|
+
ret += this.quoteValue(params[j++]);
|
|
53
|
+
pos = 1;
|
|
54
|
+
}
|
|
48
55
|
while (pos < sql.length) {
|
|
49
56
|
const idx = sql.indexOf('?', pos + 1);
|
|
50
57
|
if (idx === -1) {
|
|
51
58
|
ret += sql.substring(pos, sql.length);
|
|
52
59
|
break;
|
|
53
60
|
}
|
|
54
|
-
if (sql.
|
|
55
|
-
ret += sql.
|
|
61
|
+
if (sql.substring(idx - 1, idx + 1) === '\\?') {
|
|
62
|
+
ret += sql.substring(pos, idx - 1) + '?';
|
|
56
63
|
pos = idx + 1;
|
|
57
64
|
}
|
|
58
|
-
else if (sql.
|
|
59
|
-
ret += sql.
|
|
65
|
+
else if (sql.substring(idx, idx + 2) === '??') {
|
|
66
|
+
ret += sql.substring(pos, idx) + this.quoteIdentifier(params[j++]);
|
|
60
67
|
pos = idx + 2;
|
|
61
68
|
}
|
|
62
69
|
else {
|
|
63
|
-
ret += sql.
|
|
70
|
+
ret += sql.substring(pos, idx) + this.quoteValue(params[j++]);
|
|
64
71
|
pos = idx + 1;
|
|
65
72
|
}
|
|
66
73
|
}
|
|
@@ -69,6 +76,14 @@ class AbstractSqlPlatform extends core_1.Platform {
|
|
|
69
76
|
getSearchJsonPropertySQL(path, type, aliased) {
|
|
70
77
|
return this.getSearchJsonPropertyKey(path.split('->'), type, aliased);
|
|
71
78
|
}
|
|
79
|
+
getSearchJsonPropertyKey(path, type, aliased) {
|
|
80
|
+
const [a, ...b] = path;
|
|
81
|
+
const quoteKey = (key) => key.match(/^[a-z]\w*$/i) ? key : `"${key}"`;
|
|
82
|
+
if (aliased) {
|
|
83
|
+
return (0, core_1.raw)(alias => `json_extract(${this.quoteIdentifier(`${alias}.${a}`)}, '$.${b.map(quoteKey).join('.')}')`);
|
|
84
|
+
}
|
|
85
|
+
return (0, core_1.raw)(`json_extract(${this.quoteIdentifier(a)}, '$.${b.map(quoteKey).join('.')}')`);
|
|
86
|
+
}
|
|
72
87
|
isRaw(value) {
|
|
73
88
|
return super.isRaw(value) || (typeof value === 'object' && value !== null && value.client && ['Ref', 'Raw'].includes(value.constructor.name));
|
|
74
89
|
}
|
package/MonkeyPatchable.d.ts
CHANGED
package/MonkeyPatchable.js
CHANGED
|
@@ -13,6 +13,8 @@ const mysql_1 = __importDefault(require("knex/lib/dialects/mysql"));
|
|
|
13
13
|
// @ts-ignore
|
|
14
14
|
const mysql_columncompiler_1 = __importDefault(require("knex/lib/dialects/mysql/schema/mysql-columncompiler"));
|
|
15
15
|
// @ts-ignore
|
|
16
|
+
const mysql_querycompiler_1 = __importDefault(require("knex/lib/dialects/mysql/query/mysql-querycompiler"));
|
|
17
|
+
// @ts-ignore
|
|
16
18
|
const pg_tablecompiler_1 = __importDefault(require("knex/lib/dialects/postgres/schema/pg-tablecompiler"));
|
|
17
19
|
// @ts-ignore
|
|
18
20
|
const sqlite3_1 = __importDefault(require("knex/lib/dialects/sqlite3"));
|
|
@@ -29,6 +31,7 @@ exports.MonkeyPatchable = {
|
|
|
29
31
|
QueryExecutioner: query_executioner_1.default,
|
|
30
32
|
MySqlDialect: mysql_1.default,
|
|
31
33
|
MySqlColumnCompiler: mysql_columncompiler_1.default,
|
|
34
|
+
MySqlQueryCompiler: mysql_querycompiler_1.default,
|
|
32
35
|
PostgresDialectTableCompiler: pg_tablecompiler_1.default,
|
|
33
36
|
Sqlite3Dialect: sqlite3_1.default,
|
|
34
37
|
Sqlite3DialectTableCompiler: sqlite_tablecompiler_1.default,
|