@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.
Files changed (38) hide show
  1. package/AbstractSqlConnection.d.ts +5 -2
  2. package/AbstractSqlConnection.js +25 -17
  3. package/AbstractSqlDriver.d.ts +18 -18
  4. package/AbstractSqlDriver.js +124 -49
  5. package/AbstractSqlPlatform.d.ts +1 -0
  6. package/AbstractSqlPlatform.js +20 -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 +19 -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 +25 -7
  24. package/query/QueryBuilder.js +237 -61
  25. package/query/QueryBuilderHelper.d.ts +3 -5
  26. package/query/QueryBuilderHelper.js +97 -75
  27. package/query/ScalarCriteriaNode.d.ts +3 -2
  28. package/query/ScalarCriteriaNode.js +9 -6
  29. package/schema/DatabaseSchema.d.ts +4 -1
  30. package/schema/DatabaseSchema.js +22 -6
  31. package/schema/DatabaseTable.d.ts +2 -1
  32. package/schema/DatabaseTable.js +25 -24
  33. package/schema/SchemaComparator.js +9 -2
  34. package/schema/SchemaHelper.d.ts +5 -3
  35. package/schema/SchemaHelper.js +10 -4
  36. package/schema/SqlSchemaGenerator.d.ts +3 -5
  37. package/schema/SqlSchemaGenerator.js +41 -20
  38. package/typings.d.ts +8 -5
@@ -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);
@@ -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.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.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,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 ?? (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.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
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.reference !== core_1.ReferenceType.SCALAR;
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 true;
617
+ return !fields?.includes('*');
554
618
  }
555
- return fields.includes(prop.name);
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 [qb.raw(prop.customType.convertToJSValueSQL(prefixed, this.platform) + ' as ' + aliased).toString()];
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.reference === core_1.ReferenceType.MANY_TO_MANY && data[prop.name]) {
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, { ...options, convertCustomTypes: false, processCollections: false });
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
- Object.keys(propOrderBy).forEach(field => {
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.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner)) {
789
+ if (!prop || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner)) {
722
790
  return;
723
791
  }
724
- if (prop.reference === core_1.ReferenceType.EMBEDDED) {
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[0]);
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.customType?.convertToDatabaseValueSQL || prop.customType?.convertToJSValueSQL)
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) : ['*'];
@@ -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,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.substr(idx - 1, 2) === '\\?') {
55
- ret += sql.substr(pos, idx - pos - 1) + '?';
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.substr(idx, 2) === '??') {
59
- ret += sql.substr(pos, idx - pos) + this.quoteIdentifier(params[j++]);
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.substr(pos, idx - pos) + this.quoteValue(params[j++]);
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
  }
@@ -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;
@@ -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,