@mikro-orm/sql 7.0.0-dev.153 → 7.0.0-dev.154

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.
@@ -62,7 +62,7 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
62
62
  /**
63
63
  * @internal
64
64
  */
65
- mapPropToFieldNames<T extends object>(qb: QueryBuilder<T, any, any, any>, prop: EntityProperty<T>, tableAlias: string, explicitFields?: readonly Field<T>[]): Field<T>[];
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>[];
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: {
@@ -78,7 +78,7 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
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
79
  protected normalizeFields<T extends object>(fields: Field<T>[], prefix?: string): string[];
80
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'>): Field<T>[];
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>[];
82
82
  }
83
83
  interface FieldsForJoinedLoadOptions<T extends object> {
84
84
  explicitFields?: readonly Field<T>[];
@@ -25,9 +25,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
25
25
  const connectionType = this.resolveConnectionType({ ctx: options.ctx, connectionType: options.connectionType });
26
26
  const populate = this.autoJoinOneToOneOwner(meta, options.populate, options.fields);
27
27
  const joinedProps = this.joinedProps(meta, populate, options);
28
+ const schema = this.getSchemaName(meta, options);
28
29
  const qb = this.createQueryBuilder(meta.class, options.ctx, connectionType, false, options.logging, undefined, options.em)
29
- .withSchema(this.getSchemaName(meta, options));
30
- const fields = this.buildFields(meta, populate, joinedProps, qb, qb.alias, options);
30
+ .withSchema(schema);
31
+ const fields = this.buildFields(meta, populate, joinedProps, qb, qb.alias, options, schema);
31
32
  const orderBy = this.buildOrderBy(qb, meta, populate, options);
32
33
  const populateWhere = this.buildPopulateWhere(meta, joinedProps, options);
33
34
  Utils.asArray(options.flags).forEach(flag => qb.setFlag(flag));
@@ -361,10 +362,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
361
362
  options = { populate: [], ...options };
362
363
  const populate = options.populate;
363
364
  const joinedProps = this.joinedProps(meta, populate, options);
365
+ const schema = this.getSchemaName(meta, options);
364
366
  const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging);
365
367
  const populateWhere = this.buildPopulateWhere(meta, joinedProps, options);
366
368
  if (meta && !Utils.isEmpty(populate)) {
367
- this.buildFields(meta, populate, joinedProps, qb, qb.alias, options);
369
+ this.buildFields(meta, populate, joinedProps, qb, qb.alias, options, schema);
368
370
  }
369
371
  qb.__populateWhere = options._populateWhere;
370
372
  qb.indexHint(options.indexHint)
@@ -373,7 +375,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
373
375
  .groupBy(options.groupBy)
374
376
  .having(options.having)
375
377
  .populate(populate, joinedProps.length > 0 ? populateWhere : undefined, joinedProps.length > 0 ? options.populateFilter : undefined)
376
- .withSchema(this.getSchemaName(meta, options))
378
+ .withSchema(schema)
377
379
  .where(where);
378
380
  if (options.em) {
379
381
  await qb.applyJoinedFilters(options.em, options.filters);
@@ -1008,7 +1010,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1008
1010
  // alias all fields in the primary table
1009
1011
  meta.props
1010
1012
  .filter(prop => this.shouldHaveColumn(meta, prop, populate, options.explicitFields, options.exclude))
1011
- .forEach(prop => fields.push(...this.mapPropToFieldNames(qb, prop, options.parentTableAlias, options.explicitFields)));
1013
+ .forEach(prop => fields.push(...this.mapPropToFieldNames(qb, prop, options.parentTableAlias, meta, options.schema, options.explicitFields)));
1012
1014
  }
1013
1015
  for (const hint of joinedProps) {
1014
1016
  const [propName, ref] = hint.field.split(':', 2);
@@ -1074,14 +1076,14 @@ export class AbstractSqlDriver extends DatabaseDriver {
1074
1076
  /**
1075
1077
  * @internal
1076
1078
  */
1077
- mapPropToFieldNames(qb, prop, tableAlias, explicitFields) {
1079
+ mapPropToFieldNames(qb, prop, tableAlias, meta, schema, explicitFields) {
1078
1080
  if (prop.kind === ReferenceKind.EMBEDDED && !prop.object) {
1079
1081
  return Object.entries(prop.embeddedProps).flatMap(([name, childProp]) => {
1080
1082
  const childFields = explicitFields ? Utils.extractChildElements(explicitFields, prop.name) : [];
1081
1083
  if (!this.shouldHaveColumn(prop.targetMeta, { ...childProp, name }, [], childFields.length > 0 ? childFields : undefined)) {
1082
1084
  return [];
1083
1085
  }
1084
- return this.mapPropToFieldNames(qb, childProp, tableAlias, childFields);
1086
+ return this.mapPropToFieldNames(qb, childProp, tableAlias, meta, schema, childFields);
1085
1087
  });
1086
1088
  }
1087
1089
  const aliased = this.platform.quoteIdentifier(`${tableAlias}__${prop.fieldNames[0]}`);
@@ -1101,7 +1103,17 @@ export class AbstractSqlDriver extends DatabaseDriver {
1101
1103
  }
1102
1104
  if (prop.formula) {
1103
1105
  const alias = this.platform.quoteIdentifier(tableAlias);
1104
- return [raw(`${prop.formula(alias)} as ${aliased}`)];
1106
+ const effectiveSchema = schema ?? (meta.schema !== '*' ? meta.schema : undefined);
1107
+ const qualifiedName = effectiveSchema ? `${effectiveSchema}.${meta.tableName}` : meta.tableName;
1108
+ const table = {
1109
+ alias: alias.toString(),
1110
+ name: meta.tableName,
1111
+ schema: effectiveSchema,
1112
+ qualifiedName,
1113
+ toString: () => alias.toString(),
1114
+ };
1115
+ const columns = meta.createColumnMappingObject();
1116
+ return [raw(`${(prop.formula(table, columns))} as ${aliased}`)];
1105
1117
  }
1106
1118
  return prop.fieldNames.map(fieldName => {
1107
1119
  return `${tableAlias}.${fieldName} as ${tableAlias}__${fieldName}`;
@@ -1323,7 +1335,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1323
1335
  }
1324
1336
  ret.push(prop.name);
1325
1337
  }
1326
- buildFields(meta, populate, joinedProps, qb, alias, options) {
1338
+ buildFields(meta, populate, joinedProps, qb, alias, options, schema) {
1327
1339
  const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => this.isPopulated(meta, prop, p)));
1328
1340
  const hasLazyFormulas = meta.props.some(p => p.lazy && p.formula);
1329
1341
  const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL && p.persist !== false);
@@ -1368,6 +1380,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
1368
1380
  ret.push('*');
1369
1381
  }
1370
1382
  if (ret.length > 0 && !hasExplicitFields && addFormulas) {
1383
+ const columns = meta.createColumnMappingObject();
1384
+ const effectiveSchema = schema ?? (meta.schema !== '*' ? meta.schema : undefined);
1371
1385
  for (const prop of meta.props) {
1372
1386
  if (lazyProps.includes(prop)) {
1373
1387
  continue;
@@ -1375,7 +1389,15 @@ export class AbstractSqlDriver extends DatabaseDriver {
1375
1389
  if (prop.formula) {
1376
1390
  const a = this.platform.quoteIdentifier(alias);
1377
1391
  const aliased = this.platform.quoteIdentifier(prop.fieldNames[0]);
1378
- ret.push(raw(`${prop.formula(a)} as ${aliased}`));
1392
+ const qualifiedName = effectiveSchema ? `${effectiveSchema}.${meta.tableName}` : meta.tableName;
1393
+ const table = {
1394
+ alias: a.toString(),
1395
+ name: meta.tableName,
1396
+ schema: effectiveSchema,
1397
+ qualifiedName,
1398
+ toString: () => a.toString(),
1399
+ };
1400
+ ret.push(raw(`${(prop.formula(table, columns))} as ${aliased}`));
1379
1401
  }
1380
1402
  if (!prop.object && (prop.hasConvertToDatabaseValueSQL || prop.hasConvertToJSValueSQL)) {
1381
1403
  ret.push(prop.name);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/sql",
3
- "version": "7.0.0-dev.153",
3
+ "version": "7.0.0-dev.154",
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.153"
59
+ "@mikro-orm/core": "7.0.0-dev.154"
60
60
  }
61
61
  }
@@ -209,6 +209,8 @@ export class QueryBuilder {
209
209
  const fields = [];
210
210
  const populate = [];
211
211
  const joinKey = Object.keys(this._joins).find(join => join.endsWith(`#${alias}`));
212
+ const targetMeta = prop.targetMeta;
213
+ const schema = this._schema ?? (targetMeta.schema !== '*' ? targetMeta.schema : undefined);
212
214
  if (joinKey) {
213
215
  const path = this._joins[joinKey].path.split('.').slice(1);
214
216
  let children = this._populate;
@@ -221,29 +223,29 @@ export class QueryBuilder {
221
223
  }
222
224
  populate.push(...children);
223
225
  }
224
- for (const p of prop.targetMeta.getPrimaryProps()) {
225
- fields.push(...this.driver.mapPropToFieldNames(this, p, alias));
226
+ for (const p of targetMeta.getPrimaryProps()) {
227
+ fields.push(...this.driver.mapPropToFieldNames(this, p, alias, targetMeta, schema));
226
228
  }
227
229
  if (explicitFields) {
228
230
  for (const field of explicitFields) {
229
231
  const [a, f] = this.helper.splitField(field);
230
- const p = prop.targetMeta.properties[f];
232
+ const p = targetMeta.properties[f];
231
233
  if (p) {
232
- fields.push(...this.driver.mapPropToFieldNames(this, p, alias));
234
+ fields.push(...this.driver.mapPropToFieldNames(this, p, alias, targetMeta, schema));
233
235
  }
234
236
  else {
235
237
  fields.push(`${a}.${f} as ${a}__${f}`);
236
238
  }
237
239
  }
238
240
  }
239
- prop.targetMeta.props
241
+ targetMeta.props
240
242
  .filter(prop => {
241
243
  if (!explicitFields) {
242
244
  return this.platform.shouldHaveColumn(prop, populate);
243
245
  }
244
246
  return prop.primary && !explicitFields.includes(prop.name) && !explicitFields.includes(`${alias}.${prop.name}`);
245
247
  })
246
- .forEach(prop => fields.push(...this.driver.mapPropToFieldNames(this, prop, alias)));
248
+ .forEach(prop => fields.push(...this.driver.mapPropToFieldNames(this, prop, alias, targetMeta, schema)));
247
249
  return fields;
248
250
  }
249
251
  /**
@@ -1177,12 +1179,15 @@ export class QueryBuilder {
1177
1179
  this.processPopulateHint();
1178
1180
  this.processNestedJoins();
1179
1181
  if (meta && (this._fields?.includes('*') || this._fields?.includes(`${this.mainAlias.aliasName}.*`))) {
1182
+ const schema = this.getSchema(this.mainAlias);
1183
+ const columns = meta.createColumnMappingObject();
1180
1184
  meta.props
1181
1185
  .filter(prop => prop.formula && (!prop.lazy || this.flags.has(QueryFlag.INCLUDE_LAZY_FORMULAS)))
1182
1186
  .map(prop => {
1183
1187
  const alias = this.platform.quoteIdentifier(this.mainAlias.aliasName);
1184
1188
  const aliased = this.platform.quoteIdentifier(prop.fieldNames[0]);
1185
- return `${prop.formula(alias)} as ${aliased}`;
1189
+ const table = this.helper.createFormulaTable(alias.toString(), meta, schema);
1190
+ return `${(prop.formula(table, columns))} as ${aliased}`;
1186
1191
  })
1187
1192
  .filter(field => !this._fields.some(f => {
1188
1193
  if (isRaw(f)) {
@@ -1,4 +1,4 @@
1
- import { type Dictionary, type EntityData, type EntityKey, type EntityMetadata, type EntityName, type EntityProperty, type FlatQueryOrderMap, LockMode, type QBFilterQuery, type QBQueryOrderMap, Raw, type RawQueryFragmentSymbol } from '@mikro-orm/core';
1
+ import { type Dictionary, type EntityData, type EntityKey, type EntityMetadata, type EntityName, type EntityProperty, type FlatQueryOrderMap, type FormulaTable, LockMode, type QBFilterQuery, type QBQueryOrderMap, Raw, type RawQueryFragmentSymbol } from '@mikro-orm/core';
2
2
  import { JoinType, QueryType } from './enums.js';
3
3
  import type { Field, JoinOptions } from '../typings.js';
4
4
  import type { AbstractSqlDriver } from '../AbstractSqlDriver.js';
@@ -16,7 +16,7 @@ export declare class QueryBuilderHelper {
16
16
  private readonly metadata;
17
17
  constructor(entityName: EntityName, alias: string, aliasMap: Dictionary<Alias<any>>, subQueries: Dictionary<string>, driver: AbstractSqlDriver);
18
18
  mapper(field: string | Raw | RawQueryFragmentSymbol, type?: QueryType): string;
19
- mapper(field: string | Raw | RawQueryFragmentSymbol, type?: QueryType, value?: any, alias?: string | null): string;
19
+ mapper(field: string | Raw | RawQueryFragmentSymbol, type?: QueryType, value?: any, alias?: string | null, schema?: string): string;
20
20
  processData(data: Dictionary, convertCustomTypes: boolean, multi?: boolean): any;
21
21
  joinOneToReference(prop: EntityProperty, ownerAlias: string, alias: string, type: JoinType, cond?: Dictionary, schema?: string): JoinOptions;
22
22
  joinManyToOneReference(prop: EntityProperty, ownerAlias: string, alias: string, type: JoinType, cond?: Dictionary, schema?: string): JoinOptions;
@@ -59,6 +59,7 @@ export declare class QueryBuilderHelper {
59
59
  getProperty(field: string, alias?: string): EntityProperty | undefined;
60
60
  isTableNameAliasRequired(type: QueryType): boolean;
61
61
  processOnConflictCondition(cond: QBFilterQuery, schema?: string): QBFilterQuery;
62
+ createFormulaTable(alias: string, meta: EntityMetadata, schema?: string): FormulaTable;
62
63
  }
63
64
  export interface Alias<T> {
64
65
  aliasName: string;
@@ -21,7 +21,7 @@ export class QueryBuilderHelper {
21
21
  this.platform = this.driver.getPlatform();
22
22
  this.metadata = this.driver.getMetadata();
23
23
  }
24
- mapper(field, type = QueryType.SELECT, value, alias) {
24
+ mapper(field, type = QueryType.SELECT, value, alias, schema) {
25
25
  if (isRaw(field)) {
26
26
  return raw(field.sql, field.params);
27
27
  }
@@ -83,7 +83,10 @@ export class QueryBuilderHelper {
83
83
  const alias2 = this.platform.quoteIdentifier(a).toString();
84
84
  const aliased = this.platform.quoteIdentifier(prop.fieldNames[0]).toString();
85
85
  const as = alias === null ? '' : ` as ${aliased}`;
86
- let value = prop.formula(alias2);
86
+ const meta = this.aliasMap[a]?.meta ?? this.metadata.get(this.entityName);
87
+ const table = this.createFormulaTable(alias2, meta, schema);
88
+ const columns = meta.createColumnMappingObject();
89
+ let value = prop.formula(table, columns);
87
90
  if (!this.isTableNameAliasRequired(type)) {
88
91
  value = value.replaceAll(alias2 + '.', '');
89
92
  }
@@ -201,7 +204,10 @@ export class QueryBuilderHelper {
201
204
  const right = `${join.alias}.${join.joinColumns[idx]}`;
202
205
  if (join.prop.formula) {
203
206
  const alias = this.platform.quoteIdentifier(join.ownerAlias);
204
- const left = join.prop.formula(alias);
207
+ const ownerMeta = this.aliasMap[join.ownerAlias]?.meta ?? this.metadata.get(this.entityName);
208
+ const table = this.createFormulaTable(alias.toString(), ownerMeta, schema);
209
+ const columns = ownerMeta.createColumnMappingObject();
210
+ const left = join.prop.formula(table, columns);
205
211
  conditions.push(`${left} = ${this.platform.quoteIdentifier(right)}`);
206
212
  return;
207
213
  }
@@ -773,4 +779,15 @@ export class QueryBuilderHelper {
773
779
  }
774
780
  return cond;
775
781
  }
782
+ createFormulaTable(alias, meta, schema) {
783
+ const effectiveSchema = schema ?? (meta.schema !== '*' ? meta.schema : undefined);
784
+ const qualifiedName = effectiveSchema ? `${effectiveSchema}.${meta.tableName}` : meta.tableName;
785
+ return {
786
+ alias,
787
+ name: meta.tableName,
788
+ schema: effectiveSchema,
789
+ qualifiedName,
790
+ toString: () => alias,
791
+ };
792
+ }
776
793
  }