@mikro-orm/knex 6.0.0-dev.9 → 6.0.0-dev.91

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.
Files changed (39) hide show
  1. package/AbstractSqlConnection.d.ts +5 -2
  2. package/AbstractSqlConnection.js +25 -17
  3. package/AbstractSqlDriver.d.ts +19 -18
  4. package/AbstractSqlDriver.js +171 -70
  5. package/AbstractSqlPlatform.d.ts +1 -0
  6. package/AbstractSqlPlatform.js +26 -5
  7. package/MonkeyPatchable.d.ts +1 -0
  8. package/MonkeyPatchable.js +3 -0
  9. package/README.md +64 -107
  10. package/SqlEntityManager.d.ts +2 -5
  11. package/SqlEntityManager.js +5 -9
  12. package/SqlEntityRepository.d.ts +6 -4
  13. package/SqlEntityRepository.js +10 -8
  14. package/index.mjs +20 -6
  15. package/package.json +7 -7
  16. package/query/ArrayCriteriaNode.d.ts +3 -3
  17. package/query/CriteriaNode.d.ts +8 -8
  18. package/query/CriteriaNode.js +9 -9
  19. package/query/CriteriaNodeFactory.d.ts +6 -6
  20. package/query/CriteriaNodeFactory.js +10 -13
  21. package/query/ObjectCriteriaNode.d.ts +3 -3
  22. package/query/ObjectCriteriaNode.js +12 -8
  23. package/query/QueryBuilder.d.ts +27 -16
  24. package/query/QueryBuilder.js +250 -91
  25. package/query/QueryBuilderHelper.d.ts +6 -9
  26. package/query/QueryBuilderHelper.js +112 -96
  27. package/query/ScalarCriteriaNode.d.ts +3 -2
  28. package/query/ScalarCriteriaNode.js +9 -6
  29. package/query/enums.js +1 -1
  30. package/schema/DatabaseSchema.d.ts +4 -1
  31. package/schema/DatabaseSchema.js +22 -6
  32. package/schema/DatabaseTable.d.ts +2 -1
  33. package/schema/DatabaseTable.js +25 -24
  34. package/schema/SchemaComparator.js +9 -2
  35. package/schema/SchemaHelper.d.ts +5 -3
  36. package/schema/SchemaHelper.js +10 -4
  37. package/schema/SqlSchemaGenerator.d.ts +3 -5
  38. package/schema/SqlSchemaGenerator.js +41 -20
  39. package/typings.d.ts +10 -7
@@ -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 ?? (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 (query_1.QueryBuilder)) {
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 (query_1.QueryBuilder)) {
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(qb.raw('count(*) as count'));
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.ReferenceType.MANY_TO_MANY, core_1.ReferenceType.ONE_TO_MANY].includes(relation.reference)) {
158
- result[relation.name] = 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.ReferenceType.MANY_TO_MANY, core_1.ReferenceType.ONE_TO_MANY].includes(relation.reference)) {
183
- result[relation.name] = 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 ?? (options.convertCustomTypes = true);
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 ?? (options.processCollections = true);
239
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
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 => Object.keys(row).forEach(k => set.add(k)));
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
- const fields = core_1.Utils.flatten(props.map(prop => prop.fieldNames));
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
- params.push(...(row[prop.name] ?? prop.fieldNames.map(() => null)));
264
- keys.push(...prop.fieldNames.map(_ => '?'));
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
- params.push(row[prop.name]);
305
+ addParams(prop, row);
269
306
  }
270
307
  else {
271
- params.push(row[prop.name]);
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
- /* istanbul ignore next */
280
- const returningProps = meta.hydrateProps.filter(prop => prop.persist !== false && (prop.primary || prop.defaultRaw));
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 ?? (res.row = {});
293
- res.rows ?? (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 ?? (options.convertCustomTypes = true);
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) ? Object.keys(where) : meta.primaryKeys;
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(Object.keys(data).filter(f => !uniqueFields.includes(f)));
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 ?? (options.processCollections = true);
335
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
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
- data.forEach(row => Object.keys(row).forEach(k => keys.add(k)));
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.type.toLowerCase() === 'date') {
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.reference === core_1.ReferenceType.ONE_TO_MANY) {
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 ?? (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 cond = { [`${prop.pivotEntity}.${pivotProp2.name}`]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) } };
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 qb = this.createQueryBuilder(prop.type, ctx, options?.connectionType)
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
- qb.select(fields).populate(populate).where(where).orderBy(orderBy).setLockMode(options?.lockMode, options?.lockTableAliases);
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 = options.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.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
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.reference !== core_1.ReferenceType.SCALAR;
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 true;
645
+ return !fields?.includes('*');
554
646
  }
555
- return fields.includes(prop.name);
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 aliased = qb.ref(tableAlias ? `${tableAlias}__${prop.fieldNames[0]}` : prop.fieldNames[0]).toString();
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 = qb.ref(prop.fieldNames[0]).withSchema(tableAlias).toString();
585
- return [qb.raw(prop.customType.convertToJSValueSQL(prefixed, this.platform) + ' as ' + aliased).toString()];
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 = qb.ref(tableAlias ?? qb.alias).toString();
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 => qb.ref(fieldName).withSchema(tableAlias).as(`${tableAlias}__${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.reference === core_1.ReferenceType.MANY_TO_MANY && data[prop.name]) {
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, { ...options, convertCustomTypes: false, processCollections: false });
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).unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES).withSchema(options.schema ?? meta.schema);
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
- Object.keys(propOrderBy).forEach(field => {
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.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner)) {
815
+ if (!prop || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner)) {
722
816
  return;
723
817
  }
724
- if (prop.reference === core_1.ReferenceType.EMBEDDED) {
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[0]);
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 = qb.ref(qb.alias).toString();
782
- const aliased = qb.ref(prop.fieldNames[0]).toString();
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.customType?.convertToDatabaseValueSQL || prop.customType?.convertToJSValueSQL)
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) : ['*'];
@@ -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 */
@@ -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.substr(idx - 1, 2) === '\\?') {
55
- ret += sql.substr(pos, idx - pos - 1) + '?';
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.substr(idx, 2) === '??') {
59
- ret += sql.substr(pos, idx - pos) + this.quoteIdentifier(params[j++]);
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.substr(pos, idx - pos) + this.quoteValue(params[j++]);
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
  }
@@ -3,6 +3,7 @@ export declare const MonkeyPatchable: {
3
3
  QueryExecutioner: any;
4
4
  MySqlDialect: any;
5
5
  MySqlColumnCompiler: any;
6
+ MySqlQueryCompiler: any;
6
7
  PostgresDialectTableCompiler: any;
7
8
  Sqlite3Dialect: any;
8
9
  Sqlite3DialectTableCompiler: any;