@mikro-orm/sql 7.0.0-dev.216 → 7.0.0-dev.217

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.
@@ -5,7 +5,7 @@ import { QueryBuilder } from './query/QueryBuilder.js';
5
5
  import { type NativeQueryBuilder } from './query/NativeQueryBuilder.js';
6
6
  import { QueryType } from './query/enums.js';
7
7
  import { SqlEntityManager } from './SqlEntityManager.js';
8
- import type { Field } from './typings.js';
8
+ import type { InternalField } from './typings.js';
9
9
  export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlConnection = AbstractSqlConnection, Platform extends AbstractSqlPlatform = AbstractSqlPlatform> extends DatabaseDriver<Connection> {
10
10
  [EntityManagerType]: SqlEntityManager<this>;
11
11
  protected readonly connection: Connection;
@@ -57,12 +57,12 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
57
57
  * @internal
58
58
  */
59
59
  mergeJoinedResult<T extends object>(rawResults: EntityData<T>[], meta: EntityMetadata<T>, joinedProps: PopulateOptions<T>[]): EntityData<T>[];
60
- protected shouldHaveColumn<T, U>(meta: EntityMetadata<T>, prop: EntityProperty<U>, populate: readonly PopulateOptions<U>[], fields?: readonly Field<U>[], exclude?: readonly Field<U>[]): boolean;
61
- protected getFieldsForJoinedLoad<T extends object>(qb: QueryBuilder<T, any, any, any>, meta: EntityMetadata<T>, options: FieldsForJoinedLoadOptions<T>): Field<T>[];
60
+ protected shouldHaveColumn<T, U>(meta: EntityMetadata<T>, prop: EntityProperty<U>, populate: readonly PopulateOptions<U>[], fields?: readonly InternalField<U>[], exclude?: readonly InternalField<U>[]): boolean;
61
+ protected getFieldsForJoinedLoad<T extends object>(qb: QueryBuilder<T, any, any, any>, meta: EntityMetadata<T>, options: FieldsForJoinedLoadOptions<T>): InternalField<T>[];
62
62
  /**
63
63
  * @internal
64
64
  */
65
- mapPropToFieldNames<T extends object>(qb: QueryBuilder<T, any, any, any>, prop: EntityProperty<T>, tableAlias: string, meta: EntityMetadata<T>, schema?: string, explicitFields?: readonly Field<T>[]): Field<T>[];
65
+ mapPropToFieldNames<T extends object>(qb: QueryBuilder<T, any, any, any, any, any>, prop: EntityProperty<T>, tableAlias: string, meta: EntityMetadata<T>, schema?: string, explicitFields?: readonly InternalField<T>[]): InternalField<T>[];
66
66
  /** @internal */
67
67
  createQueryBuilder<T extends object>(entityName: EntityName<T> | QueryBuilder<T, any, any, any>, ctx?: Transaction, preferredConnectionType?: ConnectionType, convertCustomTypes?: boolean, loggerContext?: LoggingOptions, alias?: string, em?: SqlEntityManager): QueryBuilder<T, any, any, any>;
68
68
  protected resolveConnectionType(args: {
@@ -76,13 +76,13 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
76
76
  protected buildOrderBy<T extends object>(qb: QueryBuilder<T, any, any, any>, meta: EntityMetadata<T>, populate: PopulateOptions<T>[], options: Pick<FindOptions<any>, 'strategy' | 'orderBy' | 'populateOrderBy'>): QueryOrderMap<T>[];
77
77
  protected buildPopulateOrderBy<T extends object>(qb: QueryBuilder<T, any, any, any>, meta: EntityMetadata<T>, populateOrderBy: QueryOrderMap<T>[], parentPath: string, explicit: boolean, parentAlias?: string): QueryOrderMap<T>[];
78
78
  protected buildJoinedPropsOrderBy<T extends object>(qb: QueryBuilder<T, any, any, any>, meta: EntityMetadata<T>, populate: PopulateOptions<T>[], options?: Pick<FindOptions<any>, 'strategy' | 'orderBy' | 'populateOrderBy'>, parentPath?: string): QueryOrderMap<T>[];
79
- protected normalizeFields<T extends object>(fields: Field<T>[], prefix?: string): string[];
80
- protected processField<T extends object>(meta: EntityMetadata<T>, prop: EntityProperty<T> | undefined, field: string, ret: Field<T>[]): void;
81
- protected buildFields<T extends object>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb: QueryBuilder<T, any, any, any>, alias: string, options: Pick<FindOptions<T, any, any, any>, 'strategy' | 'fields' | 'exclude'>, schema?: string): Field<T>[];
79
+ protected normalizeFields<T extends object>(fields: InternalField<T>[], prefix?: string): string[];
80
+ protected processField<T extends object>(meta: EntityMetadata<T>, prop: EntityProperty<T> | undefined, field: string, ret: InternalField<T>[]): void;
81
+ protected buildFields<T extends object>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb: QueryBuilder<T, any, any, any>, alias: string, options: Pick<FindOptions<T, any, any, any>, 'strategy' | 'fields' | 'exclude'>, schema?: string): InternalField<T>[];
82
82
  }
83
83
  interface FieldsForJoinedLoadOptions<T extends object> {
84
- explicitFields?: readonly Field<T>[];
85
- exclude?: readonly Field<T>[];
84
+ explicitFields?: readonly InternalField<T>[];
85
+ exclude?: readonly InternalField<T>[];
86
86
  populate?: readonly PopulateOptions<T>[];
87
87
  strategy?: Options['loadStrategy'];
88
88
  populateWhere?: FindOptions<any>['populateWhere'];
@@ -26,8 +26,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
26
26
  const populate = this.autoJoinOneToOneOwner(meta, options.populate, options.fields);
27
27
  const joinedProps = this.joinedProps(meta, populate, options);
28
28
  const schema = this.getSchemaName(meta, options);
29
- const qb = this.createQueryBuilder(meta.class, options.ctx, connectionType, false, options.logging, undefined, options.em)
30
- .withSchema(schema);
29
+ const qb = this.createQueryBuilder(meta.class, options.ctx, connectionType, false, options.logging, undefined, options.em).withSchema(schema);
31
30
  const fields = this.buildFields(meta, populate, joinedProps, qb, qb.alias, options, schema);
32
31
  const orderBy = this.buildOrderBy(qb, meta, populate, options);
33
32
  const populateWhere = this.buildPopulateWhere(meta, joinedProps, options);
@@ -168,19 +167,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
168
167
  async wrapVirtualExpressionInSubquery(meta, expression, where, options, type) {
169
168
  const qb = await this.createQueryBuilderFromOptions(meta, where, options);
170
169
  qb.setFlag(QueryFlag.DISABLE_PAGINATE);
171
- const isCursorPagination = [
172
- options.first,
173
- options.last,
174
- options.before,
175
- options.after,
176
- ].some(v => v != null);
170
+ const isCursorPagination = [options.first, options.last, options.before, options.after].some(v => v != null);
177
171
  const native = qb.getNativeQuery(false);
178
172
  if (type === QueryType.COUNT) {
179
- native
180
- .clear('select')
181
- .clear('limit')
182
- .clear('offset')
183
- .count();
173
+ native.clear('select').clear('limit').clear('offset').count();
184
174
  }
185
175
  native.from(raw(`(${expression}) as ${this.platform.quoteIdentifier(qb.alias)}`));
186
176
  const query = native.compile();
@@ -243,7 +233,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
243
233
  // pivot ref joins via joined strategy need to be handled separately here, as they dont join the target entity
244
234
  if (pivotRefJoin) {
245
235
  let item;
246
- if (prop.inverseJoinColumns.length > 1) { // composite keys
236
+ if (prop.inverseJoinColumns.length > 1) {
237
+ // composite keys
247
238
  item = prop.inverseJoinColumns.map(name => root[`${relationAlias}__${name}`]);
248
239
  }
249
240
  else {
@@ -259,9 +250,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
259
250
  return;
260
251
  }
261
252
  const mapToPk = !hint.dataOnly && !!(ref || prop.mapToPk);
262
- const targetProps = mapToPk
263
- ? meta2.getPrimaryProps()
264
- : meta2.props.filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []));
253
+ const targetProps = mapToPk ? meta2.getPrimaryProps() : meta2.props.filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []));
265
254
  // If the primary key value for the relation is null, we know we haven't joined to anything
266
255
  // and therefore we don't return any record (since all values would be null)
267
256
  const hasPK = meta2.getPrimaryProps().every(pk => pk.fieldNames.every(name => {
@@ -286,7 +275,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
286
275
  .filter(prop => !ref && prop.persist === false && prop.fieldNames)
287
276
  .forEach(prop => {
288
277
  /* v8 ignore next */
289
- if (prop.fieldNames.length > 1) { // composite keys
278
+ if (prop.fieldNames.length > 1) {
279
+ // composite keys
290
280
  relationPojo[prop.name] = prop.fieldNames.map(name => root[`${relationAlias}__${name}`]);
291
281
  }
292
282
  else {
@@ -299,7 +289,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
299
289
  if (prop.fieldNames.every(name => typeof root[`${relationAlias}__${name}`] === 'undefined')) {
300
290
  continue;
301
291
  }
302
- if (prop.fieldNames.length > 1) { // composite keys
292
+ if (prop.fieldNames.length > 1) {
293
+ // composite keys
303
294
  const fk = prop.fieldNames.map(name => root[`${relationAlias}__${name}`]);
304
295
  const pk = Utils.mapFlatCompositePrimaryKey(fk, prop);
305
296
  relationPojo[prop.name] = pk.every(val => val != null) ? pk : null;
@@ -323,7 +314,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
323
314
  if (prop.kind === ReferenceKind.EMBEDDED && (prop.object || meta.embeddable)) {
324
315
  const item = parseJsonSafe(relationPojo[prop.name]);
325
316
  if (Array.isArray(item)) {
326
- relationPojo[prop.name] = item.map(row => row == null ? row : this.comparator.mapResult(prop.targetMeta, row));
317
+ relationPojo[prop.name] = item.map(row => (row == null ? row : this.comparator.mapResult(prop.targetMeta, row)));
327
318
  }
328
319
  else {
329
320
  relationPojo[prop.name] = item == null ? item : this.comparator.mapResult(prop.targetMeta, item);
@@ -390,7 +381,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
390
381
  const res = await this.rethrow(qb.insert(data).execute('run', false));
391
382
  res.row = res.row || {};
392
383
  let pk;
393
- if (meta.primaryKeys.length > 1) { // owner has composite pk
384
+ if (meta.primaryKeys.length > 1) {
385
+ // owner has composite pk
394
386
  pk = Utils.getPrimaryKeyCond(data, meta.primaryKeys);
395
387
  }
396
388
  else {
@@ -422,7 +414,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
422
414
  sql += fields.length > 0 ? '(' + fields.map(k => this.platform.quoteIdentifier(k)).join(', ') + ')' : `(${this.platform.quoteIdentifier(pks[0])})`;
423
415
  if (this.platform.usesOutputStatement()) {
424
416
  const returningProps = meta.props
425
- .filter(prop => prop.persist !== false && prop.defaultRaw || prop.autoincrement || prop.generated)
417
+ .filter(prop => (prop.persist !== false && prop.defaultRaw) || prop.autoincrement || prop.generated)
426
418
  .filter(prop => !(prop.name in data[0]) || isRaw(data[0][prop.name]));
427
419
  const returningFields = Utils.flatten(returningProps.map(prop => prop.fieldNames));
428
420
  sql += returningFields.length > 0 ? ` output ${returningFields.map(field => 'inserted.' + this.platform.quoteIdentifier(field)).join(', ')}` : '';
@@ -463,7 +455,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
463
455
  params.push(value);
464
456
  };
465
457
  if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
466
- sql += data.map(row => {
458
+ sql += data
459
+ .map(row => {
467
460
  const keys = [];
468
461
  const usedDups = [];
469
462
  props.forEach(prop => {
@@ -516,11 +509,12 @@ export class AbstractSqlDriver extends DatabaseDriver {
516
509
  }
517
510
  });
518
511
  return '(' + (keys.join(', ') || 'default') + ')';
519
- }).join(', ');
512
+ })
513
+ .join(', ');
520
514
  }
521
515
  if (meta && this.platform.usesReturningStatement()) {
522
516
  const returningProps = meta.props
523
- .filter(prop => prop.persist !== false && prop.defaultRaw || prop.autoincrement || prop.generated)
517
+ .filter(prop => (prop.persist !== false && prop.defaultRaw) || prop.autoincrement || prop.generated)
524
518
  .filter(prop => !(prop.name in data[0]) || isRaw(data[0][prop.name]));
525
519
  const returningFields = Utils.flatten(returningProps.map(prop => prop.fieldNames));
526
520
  /* v8 ignore next */
@@ -532,7 +526,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
532
526
  const res = await this.execute(sql, params, 'run', options.ctx, options.loggerContext);
533
527
  let pk;
534
528
  /* v8 ignore next */
535
- if (pks.length > 1) { // owner has composite pk
529
+ if (pks.length > 1) {
530
+ // owner has composite pk
536
531
  pk = data.map(d => Utils.getPrimaryKeyCond(d, pks));
537
532
  }
538
533
  else {
@@ -557,8 +552,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
557
552
  where = { [meta.primaryKeys[0] ?? pks[0]]: where };
558
553
  }
559
554
  if (Utils.hasObjectKeys(data)) {
560
- const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext)
561
- .withSchema(this.getSchemaName(meta, options));
555
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
562
556
  if (options.upsert) {
563
557
  /* v8 ignore next */
564
558
  const uniqueFields = options.onConflictFields ?? (Utils.isPlainObject(where) ? Utils.keys(where) : meta.primaryKeys);
@@ -581,9 +575,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
581
575
  qb.update(data).where(where);
582
576
  // reload generated columns and version fields
583
577
  const returning = [];
584
- meta.props
585
- .filter(prop => (prop.generated && !prop.primary) || prop.version)
586
- .forEach(prop => returning.push(prop.name));
578
+ meta.props.filter(prop => (prop.generated && !prop.primary) || prop.version).forEach(prop => returning.push(prop.name));
587
579
  qb.returning(returning);
588
580
  }
589
581
  res = await this.rethrow(qb.execute('run', false));
@@ -598,7 +590,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
598
590
  options.convertCustomTypes ??= true;
599
591
  const meta = this.metadata.get(entityName);
600
592
  if (options.upsert) {
601
- const uniqueFields = options.onConflictFields ?? (Utils.isPlainObject(where[0]) ? Object.keys(where[0]).flatMap(key => Utils.splitPrimaryKeys(key)) : meta.primaryKeys);
593
+ const uniqueFields = options.onConflictFields ??
594
+ (Utils.isPlainObject(where[0]) ? Object.keys(where[0]).flatMap(key => Utils.splitPrimaryKeys(key)) : meta.primaryKeys);
602
595
  const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
603
596
  const returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options);
604
597
  qb.insert(data)
@@ -629,10 +622,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
629
622
  }
630
623
  }
631
624
  // reload generated columns and version fields
632
- meta.props
633
- .filter(prop => prop.generated || prop.version || prop.primary)
634
- .forEach(prop => returning.add(prop.name));
635
- const pkCond = Utils.flatten(meta.primaryKeys.map(pk => meta.properties[pk].fieldNames)).map(pk => `${this.platform.quoteIdentifier(pk)} = ?`).join(' and ');
625
+ meta.props.filter(prop => prop.generated || prop.version || prop.primary).forEach(prop => returning.add(prop.name));
626
+ const pkCond = Utils.flatten(meta.primaryKeys.map(pk => meta.properties[pk].fieldNames))
627
+ .map(pk => `${this.platform.quoteIdentifier(pk)} = ?`)
628
+ .join(' and ');
636
629
  const params = [];
637
630
  let sql = `update ${this.getTableName(meta, options)} set `;
638
631
  const addParams = (prop, value) => {
@@ -727,7 +720,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
727
720
  if (Utils.isPrimaryKey(where) && pks.length === 1) {
728
721
  where = { [pks[0]]: where };
729
722
  }
730
- const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false, options.loggerContext).delete(where).withSchema(this.getSchemaName(meta, options));
723
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false, options.loggerContext)
724
+ .delete(where)
725
+ .withSchema(this.getSchemaName(meta, options));
731
726
  return this.rethrow(qb.execute('run', false));
732
727
  }
733
728
  /**
@@ -776,24 +771,22 @@ export class AbstractSqlDriver extends DatabaseDriver {
776
771
  }
777
772
  if (coll.property.kind === ReferenceKind.ONE_TO_MANY) {
778
773
  const cols = coll.property.referencedColumnNames;
779
- const qb = this.createQueryBuilder(coll.property.targetMeta.class, options?.ctx, 'write')
780
- .withSchema(this.getSchemaName(meta, options));
774
+ const qb = this.createQueryBuilder(coll.property.targetMeta.class, options?.ctx, 'write').withSchema(this.getSchemaName(meta, options));
781
775
  if (coll.getSnapshot() === undefined) {
782
776
  if (coll.property.orphanRemoval) {
783
- const query = qb.delete({ [coll.property.mappedBy]: pks })
784
- .andWhere({ [cols.join(Utils.PK_SEPARATOR)]: { $nin: insertDiff } });
777
+ const query = qb.delete({ [coll.property.mappedBy]: pks }).andWhere({ [cols.join(Utils.PK_SEPARATOR)]: { $nin: insertDiff } });
785
778
  await this.rethrow(query.execute());
786
779
  continue;
787
780
  }
788
- const query = qb.update({ [coll.property.mappedBy]: null })
781
+ const query = qb
782
+ .update({ [coll.property.mappedBy]: null })
789
783
  .where({ [coll.property.mappedBy]: pks })
790
784
  .andWhere({ [cols.join(Utils.PK_SEPARATOR)]: { $nin: insertDiff } });
791
785
  await this.rethrow(query.execute());
792
786
  continue;
793
787
  }
794
788
  /* v8 ignore next */
795
- const query = qb.update({ [coll.property.mappedBy]: pks })
796
- .where({ [cols.join(Utils.PK_SEPARATOR)]: { $in: insertDiff } });
789
+ const query = qb.update({ [coll.property.mappedBy]: pks }).where({ [cols.join(Utils.PK_SEPARATOR)]: { $in: insertDiff } });
797
790
  await this.rethrow(query.execute());
798
791
  continue;
799
792
  }
@@ -801,19 +794,19 @@ export class AbstractSqlDriver extends DatabaseDriver {
801
794
  let schema = pivotMeta.schema;
802
795
  if (schema === '*') {
803
796
  if (coll.property.owner) {
804
- schema = wrapped.getSchema() === '*' ? options?.schema ?? this.config.get('schema') : wrapped.getSchema();
797
+ schema = wrapped.getSchema() === '*' ? (options?.schema ?? this.config.get('schema')) : wrapped.getSchema();
805
798
  }
806
799
  else {
807
800
  const targetMeta = coll.property.targetMeta;
808
801
  const targetSchema = (coll[0] ?? snap?.[0]) && helper(coll[0] ?? snap?.[0]).getSchema();
809
- schema = targetMeta.schema === '*' ? options?.schema ?? targetSchema ?? this.config.get('schema') : targetMeta.schema;
802
+ schema = targetMeta.schema === '*' ? (options?.schema ?? targetSchema ?? this.config.get('schema')) : targetMeta.schema;
810
803
  }
811
804
  }
812
805
  else if (schema == null) {
813
806
  schema = this.config.get('schema');
814
807
  }
815
808
  const tableName = `${schema ?? '_'}.${pivotMeta.tableName}`;
816
- const persister = groups[tableName] ??= new PivotCollectionPersister(pivotMeta, this, options?.ctx, schema, options?.loggerContext);
809
+ const persister = (groups[tableName] ??= new PivotCollectionPersister(pivotMeta, this, options?.ctx, schema, options?.loggerContext));
817
810
  persister.enqueueUpdate(coll.property, insertDiff, deleteDiff, pks, coll.isInitialized());
818
811
  }
819
812
  for (const persister of Utils.values(groups)) {
@@ -839,20 +832,28 @@ export class AbstractSqlDriver extends DatabaseDriver {
839
832
  const populate = this.autoJoinOneToOneOwner(prop.targetMeta, options?.populate ?? [], options?.fields);
840
833
  const childFields = !Utils.isEmpty(options?.fields) ? options.fields.map(f => `${pivotProp1.name}.${f}`) : [];
841
834
  const childExclude = !Utils.isEmpty(options?.exclude) ? options.exclude.map(f => `${pivotProp1.name}.${f}`) : [];
842
- const fields = pivotJoin
843
- ? [pivotProp1.name, pivotProp2.name]
844
- : [pivotProp1.name, pivotProp2.name, ...childFields];
835
+ const fields = pivotJoin ? [pivotProp1.name, pivotProp2.name] : [pivotProp1.name, pivotProp2.name, ...childFields];
845
836
  const res = await this.find(pivotMeta.class, where, {
846
837
  ctx,
847
838
  ...options,
848
839
  fields,
849
840
  exclude: childExclude,
850
841
  orderBy: this.getPivotOrderBy(prop, pivotProp1, orderBy, options?.orderBy),
851
- populate: [{ field: populateField, strategy: LoadStrategy.JOINED, joinType: JoinType.innerJoin, children: populate, dataOnly: pivotProp1.mapToPk && !pivotJoin }],
842
+ populate: [
843
+ {
844
+ field: populateField,
845
+ strategy: LoadStrategy.JOINED,
846
+ joinType: JoinType.innerJoin,
847
+ children: populate,
848
+ dataOnly: pivotProp1.mapToPk && !pivotJoin,
849
+ },
850
+ ],
852
851
  populateWhere: undefined,
853
852
  // @ts-ignore
854
853
  _populateWhere: 'infer',
855
- populateFilter: !Utils.isEmpty(options?.populateFilter) || RawQueryFragment.hasObjectFragments(options?.populateFilter) ? { [pivotProp2.name]: options?.populateFilter } : undefined,
854
+ populateFilter: !Utils.isEmpty(options?.populateFilter) || RawQueryFragment.hasObjectFragments(options?.populateFilter)
855
+ ? { [pivotProp2.name]: options?.populateFilter }
856
+ : undefined,
856
857
  });
857
858
  const map = {};
858
859
  for (const owner of owners) {
@@ -1041,7 +1042,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1041
1042
  : (hint.filter && !prop.nullable) || mandatoryToOneProperty
1042
1043
  ? JoinType.innerJoin
1043
1044
  : JoinType.leftJoin;
1044
- const schema = prop.targetMeta.schema === '*' ? options?.schema ?? this.config.get('schema') : prop.targetMeta.schema;
1045
+ const schema = prop.targetMeta.schema === '*' ? (options?.schema ?? this.config.get('schema')) : prop.targetMeta.schema;
1045
1046
  qb.join(field, tableAlias, {}, joinType, path, schema);
1046
1047
  if (pivotRefJoin) {
1047
1048
  fields.push(...prop.joinColumns.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)), ...prop.inverseJoinColumns.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)));
@@ -1119,7 +1120,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1119
1120
  toString: () => alias.toString(),
1120
1121
  };
1121
1122
  const columns = meta.createColumnMappingObject();
1122
- return [raw(`${(prop.formula(table, columns))} as ${aliased}`)];
1123
+ return [raw(`${prop.formula(table, columns)} as ${aliased}`)];
1123
1124
  }
1124
1125
  return prop.fieldNames.map(fieldName => {
1125
1126
  return `${tableAlias}.${fieldName} as ${tableAlias}__${fieldName}`;
@@ -1171,7 +1172,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
1171
1172
  const meta = helper(entity).__meta;
1172
1173
  const qb = this.createQueryBuilder(meta.class, options.ctx, undefined, undefined, options.logging).withSchema(options.schema ?? meta.schema);
1173
1174
  const cond = Utils.getPrimaryKeyCond(entity, meta.primaryKeys);
1174
- qb.select(raw('1')).where(cond).setLockMode(options.lockMode, options.lockTableAliases);
1175
+ qb.select(raw('1'))
1176
+ .where(cond)
1177
+ .setLockMode(options.lockMode, options.lockTableAliases);
1175
1178
  await this.rethrow(qb.execute());
1176
1179
  }
1177
1180
  buildPopulateWhere(meta, joinedProps, options) {
@@ -1228,7 +1231,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
1228
1231
  }
1229
1232
  let path = parentPath;
1230
1233
  const meta2 = prop.targetMeta;
1231
- if (prop.kind !== ReferenceKind.SCALAR && (![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || !prop.owner || Utils.isPlainObject(childOrder))) {
1234
+ if (prop.kind !== ReferenceKind.SCALAR &&
1235
+ (![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || !prop.owner || Utils.isPlainObject(childOrder))) {
1232
1236
  path += `.${field}`;
1233
1237
  }
1234
1238
  if (prop.kind === ReferenceKind.MANY_TO_MANY && typeof childOrder !== 'object') {
@@ -1403,7 +1407,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1403
1407
  qualifiedName,
1404
1408
  toString: () => a.toString(),
1405
1409
  };
1406
- ret.push(raw(`${(prop.formula(table, columns))} as ${aliased}`));
1410
+ ret.push(raw(`${prop.formula(table, columns)} as ${aliased}`));
1407
1411
  }
1408
1412
  if (!prop.object && (prop.hasConvertToDatabaseValueSQL || prop.hasConvertToJSValueSQL)) {
1409
1413
  ret.push(prop.name);
@@ -1,4 +1,4 @@
1
- import { type Constructor, type EntityManager, type EntityRepository, type IDatabaseDriver, type IsolationLevel, type MikroORM, Platform } from '@mikro-orm/core';
1
+ import { type RawQueryFragment, type Constructor, type EntityManager, type EntityRepository, type IDatabaseDriver, type IsolationLevel, type MikroORM, Platform } from '@mikro-orm/core';
2
2
  import { SqlSchemaGenerator } from './schema/SqlSchemaGenerator.js';
3
3
  import { type SchemaHelper } from './schema/SchemaHelper.js';
4
4
  import type { IndexDef } from './typings.js';
@@ -24,8 +24,8 @@ export declare abstract class AbstractSqlPlatform extends Platform {
24
24
  getRollbackToSavepointSQL(savepointName: string): string;
25
25
  getReleaseSavepointSQL(savepointName: string): string;
26
26
  quoteValue(value: any): string;
27
- getSearchJsonPropertySQL(path: string, type: string, aliased: boolean): string;
28
- getSearchJsonPropertyKey(path: string[], type: string, aliased: boolean, value?: unknown): string;
27
+ getSearchJsonPropertySQL(path: string, type: string, aliased: boolean): string | RawQueryFragment;
28
+ getSearchJsonPropertyKey(path: string[], type: string, aliased: boolean, value?: unknown): string | RawQueryFragment;
29
29
  getJsonIndexDefinition(index: IndexDef): string[];
30
30
  supportsSchemas(): boolean;
31
31
  /** @inheritDoc */
@@ -20,7 +20,7 @@ export declare class SqlEntityManager<Driver extends AbstractSqlDriver = Abstrac
20
20
  /**
21
21
  * Shortcut for `createQueryBuilder()`
22
22
  */
23
- qb<Entity extends object, RootAlias extends string = never>(entityName: EntityName<Entity>, alias?: RootAlias, type?: ConnectionType, loggerContext?: LoggingOptions): QueryBuilder<Entity, RootAlias, never, never>;
23
+ qb<Entity extends object, RootAlias extends string = never>(entityName: EntityName<Entity>, alias?: RootAlias, type?: ConnectionType, loggerContext?: LoggingOptions): QueryBuilder<Entity, RootAlias, never, never, never, "*">;
24
24
  /**
25
25
  * Returns configured Kysely instance.
26
26
  */
@@ -1,4 +1,4 @@
1
- import { type EntityProperty, type IsolationLevel, type SimpleColumnMeta, Type } from '@mikro-orm/core';
1
+ import { type EntityProperty, type IsolationLevel, RawQueryFragment, type SimpleColumnMeta, Type } from '@mikro-orm/core';
2
2
  import { AbstractSqlPlatform } from '../../AbstractSqlPlatform.js';
3
3
  import type { IndexDef } from '../../typings.js';
4
4
  import { PostgreSqlNativeQueryBuilder } from './PostgreSqlNativeQueryBuilder.js';
@@ -78,7 +78,7 @@ export declare class BasePostgreSqlPlatform extends AbstractSqlPlatform {
78
78
  }): string;
79
79
  getBlobDeclarationSQL(): string;
80
80
  getJsonDeclarationSQL(): string;
81
- getSearchJsonPropertyKey(path: string[], type: string | undefined | Type, aliased: boolean, value?: unknown): string;
81
+ getSearchJsonPropertyKey(path: string[], type: string | undefined | Type, aliased: boolean, value?: unknown): string | RawQueryFragment;
82
82
  getJsonIndexDefinition(index: IndexDef): string[];
83
83
  quoteIdentifier(id: string | {
84
84
  toString: () => string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/sql",
3
- "version": "7.0.0-dev.216",
3
+ "version": "7.0.0-dev.217",
4
4
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -56,6 +56,6 @@
56
56
  "@mikro-orm/core": "^6.6.4"
57
57
  },
58
58
  "peerDependencies": {
59
- "@mikro-orm/core": "7.0.0-dev.216"
59
+ "@mikro-orm/core": "7.0.0-dev.217"
60
60
  }
61
61
  }
@@ -10,6 +10,7 @@ export declare class CriteriaNode<T extends object> implements ICriteriaNode<T>
10
10
  readonly entityName: EntityName<T>;
11
11
  readonly parent?: ICriteriaNode<T> | undefined;
12
12
  readonly key?: (EntityKey<T> | RawQueryFragmentSymbol) | undefined;
13
+ readonly validate: boolean;
13
14
  readonly strict: boolean;
14
15
  payload: any;
15
16
  prop?: EntityProperty<T>;
@@ -9,6 +9,7 @@ export class CriteriaNode {
9
9
  entityName;
10
10
  parent;
11
11
  key;
12
+ validate;
12
13
  strict;
13
14
  payload;
14
15
  prop;
@@ -18,6 +19,7 @@ export class CriteriaNode {
18
19
  this.entityName = entityName;
19
20
  this.parent = parent;
20
21
  this.key = key;
22
+ this.validate = validate;
21
23
  this.strict = strict;
22
24
  const meta = parent && metadata.find(parent.entityName);
23
25
  if (meta && key && !RawQueryFragment.isKnownFragmentSymbol(key)) {
@@ -4,9 +4,9 @@ import type { ICriteriaNode } from '../typings.js';
4
4
  * @internal
5
5
  */
6
6
  export declare class CriteriaNodeFactory {
7
- static createNode<T extends object>(metadata: MetadataStorage, entityName: EntityName<T>, payload: any, parent?: ICriteriaNode<T>, key?: EntityKey<T> | RawQueryFragmentSymbol): ICriteriaNode<T>;
8
- static createScalarNode<T extends object>(metadata: MetadataStorage, entityName: EntityName<T>, payload: any, parent?: ICriteriaNode<T>, key?: EntityKey<T> | RawQueryFragmentSymbol): ICriteriaNode<T>;
9
- static createArrayNode<T extends object>(metadata: MetadataStorage, entityName: EntityName<T>, payload: any[], parent?: ICriteriaNode<T>, key?: EntityKey<T>): ICriteriaNode<T>;
10
- static createObjectNode<T extends object>(metadata: MetadataStorage, entityName: EntityName<T>, payload: Dictionary, parent?: ICriteriaNode<T>, key?: EntityKey<T>): ICriteriaNode<T>;
11
- static createObjectItemNode<T extends object>(metadata: MetadataStorage, entityName: EntityName<T>, node: ICriteriaNode<T>, payload: Dictionary, key: EntityKey<T> | RawQueryFragmentSymbol, meta?: EntityMetadata<T>): ICriteriaNode<T>;
7
+ static createNode<T extends object>(metadata: MetadataStorage, entityName: EntityName<T>, payload: any, parent?: ICriteriaNode<T>, key?: EntityKey<T> | RawQueryFragmentSymbol, validate?: boolean): ICriteriaNode<T>;
8
+ static createScalarNode<T extends object>(metadata: MetadataStorage, entityName: EntityName<T>, payload: any, parent?: ICriteriaNode<T>, key?: EntityKey<T> | RawQueryFragmentSymbol, validate?: boolean): ICriteriaNode<T>;
9
+ static createArrayNode<T extends object>(metadata: MetadataStorage, entityName: EntityName<T>, payload: any[], parent?: ICriteriaNode<T>, key?: EntityKey<T>, validate?: boolean): ICriteriaNode<T>;
10
+ static createObjectNode<T extends object>(metadata: MetadataStorage, entityName: EntityName<T>, payload: Dictionary, parent?: ICriteriaNode<T>, key?: EntityKey<T>, validate?: boolean): ICriteriaNode<T>;
11
+ static createObjectItemNode<T extends object>(metadata: MetadataStorage, entityName: EntityName<T>, node: ICriteriaNode<T>, payload: Dictionary, key: EntityKey<T> | RawQueryFragmentSymbol, meta?: EntityMetadata<T>, validate?: boolean): ICriteriaNode<T>;
12
12
  }
@@ -6,26 +6,26 @@ import { ScalarCriteriaNode } from './ScalarCriteriaNode.js';
6
6
  * @internal
7
7
  */
8
8
  export class CriteriaNodeFactory {
9
- static createNode(metadata, entityName, payload, parent, key) {
9
+ static createNode(metadata, entityName, payload, parent, key, validate = true) {
10
10
  const rawField = RawQueryFragment.isKnownFragmentSymbol(key);
11
11
  const scalar = Utils.isPrimaryKey(payload) || isRaw(payload) || payload instanceof RegExp || payload instanceof Date || rawField;
12
12
  if (Array.isArray(payload) && !scalar) {
13
- return this.createArrayNode(metadata, entityName, payload, parent, key);
13
+ return this.createArrayNode(metadata, entityName, payload, parent, key, validate);
14
14
  }
15
15
  if (Utils.isPlainObject(payload) && !scalar) {
16
- return this.createObjectNode(metadata, entityName, payload, parent, key);
16
+ return this.createObjectNode(metadata, entityName, payload, parent, key, validate);
17
17
  }
18
- return this.createScalarNode(metadata, entityName, payload, parent, key);
18
+ return this.createScalarNode(metadata, entityName, payload, parent, key, validate);
19
19
  }
20
- static createScalarNode(metadata, entityName, payload, parent, key) {
21
- const node = new ScalarCriteriaNode(metadata, entityName, parent, key);
20
+ static createScalarNode(metadata, entityName, payload, parent, key, validate = true) {
21
+ const node = new ScalarCriteriaNode(metadata, entityName, parent, key, validate);
22
22
  node.payload = payload;
23
23
  return node;
24
24
  }
25
- static createArrayNode(metadata, entityName, payload, parent, key) {
26
- const node = new ArrayCriteriaNode(metadata, entityName, parent, key);
25
+ static createArrayNode(metadata, entityName, payload, parent, key, validate = true) {
26
+ const node = new ArrayCriteriaNode(metadata, entityName, parent, key, validate);
27
27
  node.payload = payload.map((item, index) => {
28
- const n = this.createNode(metadata, entityName, item, node);
28
+ const n = this.createNode(metadata, entityName, item, node, undefined, validate);
29
29
  // we care about branching only for $and
30
30
  if (key === '$and' && payload.length > 1) {
31
31
  n.index = index;
@@ -34,36 +34,36 @@ export class CriteriaNodeFactory {
34
34
  });
35
35
  return node;
36
36
  }
37
- static createObjectNode(metadata, entityName, payload, parent, key) {
37
+ static createObjectNode(metadata, entityName, payload, parent, key, validate = true) {
38
38
  const meta = metadata.find(entityName);
39
- const node = new ObjectCriteriaNode(metadata, entityName, parent, key, true, payload.__strict);
39
+ const node = new ObjectCriteriaNode(metadata, entityName, parent, key, validate, payload.__strict);
40
40
  node.payload = {};
41
41
  for (const k of Utils.getObjectQueryKeys(payload)) {
42
- node.payload[k] = this.createObjectItemNode(metadata, entityName, node, payload, k, meta);
42
+ node.payload[k] = this.createObjectItemNode(metadata, entityName, node, payload, k, meta, validate);
43
43
  }
44
44
  return node;
45
45
  }
46
- static createObjectItemNode(metadata, entityName, node, payload, key, meta) {
46
+ static createObjectItemNode(metadata, entityName, node, payload, key, meta, validate = true) {
47
47
  const rawField = RawQueryFragment.isKnownFragmentSymbol(key);
48
48
  const prop = rawField ? null : meta?.properties[key];
49
49
  const childEntity = prop && prop.kind !== ReferenceKind.SCALAR ? prop.targetMeta.class : entityName;
50
50
  const isNotEmbedded = rawField || prop?.kind !== ReferenceKind.EMBEDDED;
51
51
  const val = payload[key];
52
52
  if (isNotEmbedded && prop?.customType instanceof JsonType) {
53
- return this.createScalarNode(metadata, childEntity, val, node, key);
53
+ return this.createScalarNode(metadata, childEntity, val, node, key, validate);
54
54
  }
55
55
  if (prop?.kind === ReferenceKind.SCALAR && val != null && Object.keys(val).some(f => f in GroupOperator)) {
56
56
  throw ValidationError.cannotUseGroupOperatorsInsideScalars(entityName, prop.name, payload);
57
57
  }
58
58
  if (isNotEmbedded) {
59
- return this.createNode(metadata, childEntity, val, node, key);
59
+ return this.createNode(metadata, childEntity, val, node, key, validate);
60
60
  }
61
61
  if (val == null) {
62
62
  const map = Object.keys(prop.embeddedProps).reduce((oo, k) => {
63
63
  oo[prop.embeddedProps[k].name] = null;
64
64
  return oo;
65
65
  }, {});
66
- return this.createNode(metadata, entityName, map, node, key);
66
+ return this.createNode(metadata, entityName, map, node, key, validate);
67
67
  }
68
68
  // array operators can be used on embedded properties
69
69
  const allowedOperators = ['$contains', '$contained', '$overlap'];
@@ -87,6 +87,6 @@ export class CriteriaNodeFactory {
87
87
  }
88
88
  return oo;
89
89
  }, {});
90
- return this.createNode(metadata, entityName, map, node, key);
90
+ return this.createNode(metadata, entityName, map, node, key, validate);
91
91
  }
92
92
  }
@@ -1,4 +1,4 @@
1
- import { type Dictionary, LockMode, type QueryFlag, RawQueryFragment } from '@mikro-orm/core';
1
+ import { type Dictionary, LockMode, type QueryFlag, RawQueryFragment, type Subquery } from '@mikro-orm/core';
2
2
  import { QueryType } from './enums.js';
3
3
  import type { AbstractSqlPlatform } from '../AbstractSqlPlatform.js';
4
4
  interface Options {
@@ -48,8 +48,9 @@ interface OnConflictClause {
48
48
  };
49
49
  }
50
50
  /** @internal */
51
- export declare class NativeQueryBuilder {
51
+ export declare class NativeQueryBuilder implements Subquery {
52
52
  protected readonly platform: AbstractSqlPlatform;
53
+ readonly __subquery: true;
53
54
  protected type?: QueryType;
54
55
  protected parts: string[];
55
56
  protected params: unknown[];
@@ -106,6 +106,11 @@ export class ObjectCriteriaNode extends CriteriaNode {
106
106
  const rawField = RawQueryFragment.getKnownFragment(field);
107
107
  o[raw(rawField.sql.replaceAll(ALIAS_REPLACEMENT, alias), rawField.params)] = payload;
108
108
  }
109
+ else if (!childNode.validate && !childNode.prop && !field.includes('.') && !operator) {
110
+ // wrap unknown fields in raw() to prevent alias prefixing (e.g. raw SQL aliases in HAVING)
111
+ // use '??' placeholder to properly quote the identifier
112
+ o[raw('??', [field])] = payload;
113
+ }
109
114
  else if (primaryKey || virtual || operator || field.includes('.') || ![QueryType.SELECT, QueryType.COUNT].includes(qb.type)) {
110
115
  this.inlineCondition(field.replaceAll(ALIAS_REPLACEMENT, alias), o, payload);
111
116
  }