@mikro-orm/knex 6.0.0-dev.4 → 6.0.0-dev.6

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 (53) hide show
  1. package/{dist/AbstractSqlConnection.d.ts → AbstractSqlConnection.d.ts} +0 -0
  2. package/{dist/AbstractSqlConnection.js → AbstractSqlConnection.js} +5 -0
  3. package/AbstractSqlDriver.d.ts +66 -0
  4. package/{dist/AbstractSqlDriver.js → AbstractSqlDriver.js} +218 -70
  5. package/{dist/AbstractSqlPlatform.d.ts → AbstractSqlPlatform.d.ts} +6 -6
  6. package/{dist/AbstractSqlPlatform.js → AbstractSqlPlatform.js} +12 -14
  7. package/{dist/MonkeyPatchable.d.ts → MonkeyPatchable.d.ts} +0 -0
  8. package/{dist/MonkeyPatchable.js → MonkeyPatchable.js} +0 -0
  9. package/{dist/README.md → README.md} +2 -2
  10. package/{dist/SqlEntityManager.d.ts → SqlEntityManager.d.ts} +5 -5
  11. package/{dist/SqlEntityManager.js → SqlEntityManager.js} +0 -1
  12. package/{dist/SqlEntityRepository.d.ts → SqlEntityRepository.d.ts} +3 -3
  13. package/{dist/SqlEntityRepository.js → SqlEntityRepository.js} +1 -1
  14. package/{dist/index.d.ts → index.d.ts} +0 -0
  15. package/{dist/index.js → index.js} +5 -1
  16. package/{dist/index.mjs → index.mjs} +0 -0
  17. package/package.json +18 -47
  18. package/{dist/query → query}/ArrayCriteriaNode.d.ts +0 -0
  19. package/{dist/query → query}/ArrayCriteriaNode.js +0 -0
  20. package/{dist/query → query}/CriteriaNode.d.ts +0 -0
  21. package/{dist/query → query}/CriteriaNode.js +7 -4
  22. package/{dist/query → query}/CriteriaNodeFactory.d.ts +0 -0
  23. package/{dist/query → query}/CriteriaNodeFactory.js +0 -0
  24. package/{dist/query → query}/ObjectCriteriaNode.d.ts +0 -0
  25. package/{dist/query → query}/ObjectCriteriaNode.js +14 -1
  26. package/{dist/query → query}/QueryBuilder.d.ts +58 -24
  27. package/{dist/query → query}/QueryBuilder.js +285 -84
  28. package/{dist/query → query}/QueryBuilderHelper.d.ts +19 -11
  29. package/{dist/query → query}/QueryBuilderHelper.js +206 -108
  30. package/{dist/query → query}/ScalarCriteriaNode.d.ts +0 -0
  31. package/{dist/query → query}/ScalarCriteriaNode.js +0 -0
  32. package/{dist/query → query}/enums.d.ts +0 -0
  33. package/{dist/query → query}/enums.js +0 -0
  34. package/{dist/query → query}/index.d.ts +0 -0
  35. package/{dist/query → query}/index.js +5 -1
  36. package/{dist/schema → schema}/DatabaseSchema.d.ts +1 -1
  37. package/{dist/schema → schema}/DatabaseSchema.js +8 -16
  38. package/{dist/schema → schema}/DatabaseTable.d.ts +1 -1
  39. package/{dist/schema → schema}/DatabaseTable.js +2 -1
  40. package/{dist/schema → schema}/SchemaComparator.d.ts +3 -1
  41. package/{dist/schema → schema}/SchemaComparator.js +58 -10
  42. package/{dist/schema → schema}/SchemaGenerator.d.ts +11 -9
  43. package/{dist/schema → schema}/SchemaGenerator.js +81 -28
  44. package/{dist/schema → schema}/SchemaHelper.d.ts +9 -4
  45. package/{dist/schema → schema}/SchemaHelper.js +46 -8
  46. package/{dist/schema → schema}/index.d.ts +0 -0
  47. package/{dist/schema → schema}/index.js +5 -1
  48. package/{dist/typings.d.ts → typings.d.ts} +7 -3
  49. package/{dist/typings.js → typings.js} +0 -0
  50. package/CHANGELOG.md +0 -661
  51. package/dist/AbstractSqlDriver.d.ts +0 -55
  52. package/dist/LICENSE +0 -21
  53. package/dist/package.json +0 -99
@@ -102,6 +102,11 @@ class AbstractSqlConnection extends core_1.Connection {
102
102
  await this.client.raw(buf.toString());
103
103
  }
104
104
  createKnexClient(type) {
105
+ const driverOptions = this.config.get('driverOptions');
106
+ if (driverOptions.context?.client instanceof knex_1.knex.Client) {
107
+ this.logger.log('info', 'Reusing knex client provided via `driverOptions`');
108
+ return driverOptions;
109
+ }
105
110
  return (0, knex_1.knex)(this.getKnexOptions(type))
106
111
  .on('query', data => {
107
112
  if (!data.__knexQueryUid) {
@@ -0,0 +1,66 @@
1
+ import type { Knex } from 'knex';
2
+ import type { AnyEntity, Collection, ConnectionType, Configuration, Constructor, CountOptions, DeleteOptions, Dictionary, DriverMethodOptions, EntityData, EntityDictionary, EntityField, EntityMetadata, EntityName, EntityProperty, FilterQuery, FindOneOptions, FindOptions, IDatabaseDriver, LockOptions, NativeInsertUpdateManyOptions, NativeInsertUpdateOptions, PopulateOptions, Primary, QueryOrderMap, QueryResult, Transaction } from '@mikro-orm/core';
3
+ import { DatabaseDriver, EntityManagerType } from '@mikro-orm/core';
4
+ import type { AbstractSqlConnection } from './AbstractSqlConnection';
5
+ import type { AbstractSqlPlatform } from './AbstractSqlPlatform';
6
+ import { QueryBuilder, QueryType } from './query';
7
+ import { SqlEntityManager } from './SqlEntityManager';
8
+ import type { Field } from './typings';
9
+ export declare abstract class AbstractSqlDriver<C extends AbstractSqlConnection = AbstractSqlConnection> extends DatabaseDriver<C> {
10
+ [EntityManagerType]: SqlEntityManager<this>;
11
+ protected readonly connection: C;
12
+ protected readonly replicas: C[];
13
+ protected readonly platform: AbstractSqlPlatform;
14
+ protected constructor(config: Configuration, platform: AbstractSqlPlatform, connection: Constructor<C>, connector: string[]);
15
+ getPlatform(): AbstractSqlPlatform;
16
+ createEntityManager<D extends IDatabaseDriver = IDatabaseDriver>(useContext?: boolean): D[typeof EntityManagerType];
17
+ find<T extends object, P extends string = never>(entityName: string, where: FilterQuery<T>, options?: FindOptions<T, P>): Promise<EntityData<T>[]>;
18
+ findOne<T extends object, P extends string = never>(entityName: string, where: FilterQuery<T>, options?: FindOneOptions<T, P>): Promise<EntityData<T> | null>;
19
+ findVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: FindOptions<T, any>): Promise<EntityData<T>[]>;
20
+ countVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: CountOptions<T>): Promise<number>;
21
+ protected wrapVirtualExpressionInSubquery<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any>, type: QueryType.COUNT): Promise<number>;
22
+ protected wrapVirtualExpressionInSubquery<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any>, type: QueryType.SELECT): Promise<T[]>;
23
+ protected wrapVirtualExpressionInSubquery<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any>): Promise<T[]>;
24
+ mapResult<T extends object>(result: EntityData<T>, meta: EntityMetadata<T>, populate?: PopulateOptions<T>[], qb?: QueryBuilder<T>, map?: Dictionary): EntityData<T> | null;
25
+ private mapJoinedProps;
26
+ private appendToCollection;
27
+ count<T extends object>(entityName: string, where: any, options?: CountOptions<T>): Promise<number>;
28
+ nativeInsert<T extends object>(entityName: string, data: EntityDictionary<T>, options?: NativeInsertUpdateOptions<T>): Promise<QueryResult<T>>;
29
+ nativeInsertMany<T extends object>(entityName: string, data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T>): Promise<QueryResult<T>>;
30
+ nativeUpdate<T extends object>(entityName: string, where: FilterQuery<T>, data: EntityDictionary<T>, options?: NativeInsertUpdateOptions<T>): Promise<QueryResult<T>>;
31
+ nativeUpdateMany<T extends object>(entityName: string, where: FilterQuery<T>[], data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T>): Promise<QueryResult<T>>;
32
+ nativeDelete<T extends object>(entityName: string, where: FilterQuery<T> | string | any, options?: DeleteOptions<T>): Promise<QueryResult<T>>;
33
+ syncCollection<T extends object, O extends object>(coll: Collection<T, O>, options?: DriverMethodOptions): Promise<void>;
34
+ loadFromPivotTable<T extends object, O extends object>(prop: EntityProperty, owners: Primary<O>[][], where?: FilterQuery<any>, orderBy?: QueryOrderMap<T>[], ctx?: Transaction, options?: FindOptions<T, any>): Promise<Dictionary<T[]>>;
35
+ execute<T extends QueryResult | EntityData<AnyEntity> | EntityData<AnyEntity>[] = EntityData<AnyEntity>[]>(queryOrKnex: string | Knex.QueryBuilder | Knex.Raw, params?: any[], method?: 'all' | 'get' | 'run', ctx?: Transaction): Promise<T>;
36
+ /**
37
+ * 1:1 owner side needs to be marked for population so QB auto-joins the owner id
38
+ */
39
+ protected autoJoinOneToOneOwner<T extends object, P extends string = never>(meta: EntityMetadata, populate: PopulateOptions<T>[], fields?: readonly EntityField<T, P>[]): PopulateOptions<T>[];
40
+ protected joinedProps<T>(meta: EntityMetadata, populate: PopulateOptions<T>[]): PopulateOptions<T>[];
41
+ /**
42
+ * @internal
43
+ */
44
+ mergeJoinedResult<T extends object>(rawResults: EntityData<T>[], meta: EntityMetadata<T>): EntityData<T>[];
45
+ protected getFieldsForJoinedLoad<T extends object>(qb: QueryBuilder<T>, meta: EntityMetadata<T>, explicitFields?: Field<T>[], populate?: PopulateOptions<T>[], parentTableAlias?: string, parentJoinPath?: string): Field<T>[];
46
+ /**
47
+ * @internal
48
+ */
49
+ mapPropToFieldNames<T extends object>(qb: QueryBuilder<T>, prop: EntityProperty<T>, tableAlias?: string): Field<T>[];
50
+ /** @internal */
51
+ createQueryBuilder<T extends object>(entityName: EntityName<T> | QueryBuilder<T>, ctx?: Transaction<Knex.Transaction>, preferredConnectionType?: ConnectionType, convertCustomTypes?: boolean): QueryBuilder<T>;
52
+ protected resolveConnectionType(args: {
53
+ ctx?: Transaction<Knex.Transaction>;
54
+ connectionType?: ConnectionType;
55
+ }): ConnectionType;
56
+ protected extractManyToMany<T>(entityName: string, data: EntityDictionary<T>): EntityData<T>;
57
+ protected processManyToMany<T extends object>(meta: EntityMetadata<T> | undefined, pks: Primary<T>[], collections: EntityData<T>, clear: boolean, options?: DriverMethodOptions): Promise<void>;
58
+ protected updateCollectionDiff<T extends object, O extends object>(meta: EntityMetadata<O>, prop: EntityProperty<T>, pks: Primary<O>[], deleteDiff: Primary<T>[][] | boolean, insertDiff: Primary<T>[][], options?: DriverMethodOptions & {
59
+ ownerSchema?: string;
60
+ }): Promise<void>;
61
+ lockPessimistic<T>(entity: T, options: LockOptions): Promise<void>;
62
+ protected buildJoinedPropsOrderBy<T extends object>(entityName: string, qb: QueryBuilder<T>, meta: EntityMetadata<T>, populate: PopulateOptions<T>[], parentPath?: string): QueryOrderMap<T>[];
63
+ protected normalizeFields<T extends object>(fields: Field<T>[], prefix?: string): string[];
64
+ protected processField<T extends object>(meta: EntityMetadata<T>, prop: EntityProperty<T> | undefined, field: string, ret: Field<T>[], populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb: QueryBuilder<T>): void;
65
+ protected buildFields<T extends object>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb: QueryBuilder<T>, fields?: Field<T>[]): Field<T>[];
66
+ }
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AbstractSqlDriver = void 0;
4
4
  const core_1 = require("@mikro-orm/core");
5
- const QueryBuilder_1 = require("./query/QueryBuilder");
5
+ const query_1 = require("./query");
6
6
  const SqlEntityManager_1 = require("./SqlEntityManager");
7
7
  class AbstractSqlDriver extends core_1.DatabaseDriver {
8
8
  constructor(config, platform, connection, connector) {
@@ -21,16 +21,19 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
21
21
  async find(entityName, where, options = {}) {
22
22
  options = { populate: [], orderBy: [], ...options };
23
23
  const meta = this.metadata.find(entityName);
24
+ if (meta?.virtual) {
25
+ return this.findVirtual(entityName, where, options);
26
+ }
24
27
  const populate = this.autoJoinOneToOneOwner(meta, options.populate, options.fields);
25
28
  const joinedProps = this.joinedProps(meta, populate);
26
- const qb = this.createQueryBuilder(entityName, options.ctx, !!options.ctx, false);
29
+ const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false);
27
30
  const fields = this.buildFields(meta, populate, joinedProps, qb, options.fields);
28
31
  const joinedPropsOrderBy = this.buildJoinedPropsOrderBy(entityName, qb, meta, joinedProps);
29
32
  if (core_1.Utils.isPrimaryKey(where, meta.compositePK)) {
30
33
  where = { [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
31
34
  }
32
35
  qb.select(fields)
33
- .populate(populate)
36
+ .populate(populate, joinedProps.length > 0 ? options.populateWhere : undefined)
34
37
  .where(where)
35
38
  .orderBy([...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy])
36
39
  .groupBy(options.groupBy)
@@ -57,9 +60,70 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
57
60
  if (joinedProps.length === 0) {
58
61
  opts.limit = 1;
59
62
  }
63
+ if (opts.limit > 0 && !opts.flags?.includes(core_1.QueryFlag.DISABLE_PAGINATE)) {
64
+ opts.flags ?? (opts.flags = []);
65
+ opts.flags.push(core_1.QueryFlag.DISABLE_PAGINATE);
66
+ }
60
67
  const res = await this.find(entityName, where, opts);
61
68
  return res[0] || null;
62
69
  }
70
+ async findVirtual(entityName, where, options) {
71
+ const meta = this.metadata.get(entityName);
72
+ /* istanbul ignore next */
73
+ if (!meta.expression) {
74
+ return [];
75
+ }
76
+ if (typeof meta.expression === 'string') {
77
+ return this.wrapVirtualExpressionInSubquery(meta, meta.expression, where, options);
78
+ }
79
+ const em = this.createEntityManager(false);
80
+ em.setTransactionContext(options.ctx);
81
+ const res = meta.expression(em, where, options);
82
+ if (res instanceof (query_1.QueryBuilder)) {
83
+ return this.wrapVirtualExpressionInSubquery(meta, res.getFormattedQuery(), where, options);
84
+ }
85
+ /* istanbul ignore next */
86
+ return res;
87
+ }
88
+ async countVirtual(entityName, where, options) {
89
+ const meta = this.metadata.get(entityName);
90
+ /* istanbul ignore next */
91
+ if (!meta.expression) {
92
+ return 0;
93
+ }
94
+ if (typeof meta.expression === 'string') {
95
+ return this.wrapVirtualExpressionInSubquery(meta, meta.expression, where, options, query_1.QueryType.COUNT);
96
+ }
97
+ const em = this.createEntityManager(false);
98
+ em.setTransactionContext(options.ctx);
99
+ const res = meta.expression(em, where, options);
100
+ if (res instanceof (query_1.QueryBuilder)) {
101
+ return this.wrapVirtualExpressionInSubquery(meta, res.getFormattedQuery(), where, options, query_1.QueryType.COUNT);
102
+ }
103
+ /* istanbul ignore next */
104
+ return res;
105
+ }
106
+ async wrapVirtualExpressionInSubquery(meta, expression, where, options, type = query_1.QueryType.SELECT) {
107
+ const qb = this.createQueryBuilder(meta.className, options?.ctx, options.connectionType, options.convertCustomTypes)
108
+ .limit(options?.limit, options?.offset);
109
+ if (options.orderBy) {
110
+ qb.orderBy(options.orderBy);
111
+ }
112
+ qb.where(where);
113
+ const kqb = qb.getKnexQuery().clear('select');
114
+ if (type === query_1.QueryType.COUNT) {
115
+ kqb.select(qb.raw('count(*) as count'));
116
+ }
117
+ else { // select
118
+ kqb.select('*');
119
+ }
120
+ kqb.fromRaw(`(${expression}) as ${this.platform.quoteIdentifier(qb.alias)}`);
121
+ const res = await this.execute(kqb);
122
+ if (type === query_1.QueryType.COUNT) {
123
+ return res[0].count;
124
+ }
125
+ return res.map(row => this.mapResult(row, meta));
126
+ }
63
127
  mapResult(result, meta, populate = [], qb, map = {}) {
64
128
  const ret = super.mapResult(result, meta);
65
129
  /* istanbul ignore if */
@@ -137,23 +201,25 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
137
201
  collection.push(relationPojo);
138
202
  }
139
203
  }
140
- async count(entityName, where, options = {}, ctx) {
204
+ async count(entityName, where, options = {}) {
141
205
  const meta = this.metadata.find(entityName);
142
- const pks = meta.primaryKeys;
143
- const qb = this.createQueryBuilder(entityName, ctx, !!ctx, false)
206
+ if (meta?.virtual) {
207
+ return this.countVirtual(entityName, where, options);
208
+ }
209
+ const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false)
144
210
  .groupBy(options.groupBy)
145
211
  .having(options.having)
146
212
  .populate(options.populate ?? [])
147
213
  .withSchema(this.getSchemaName(meta, options))
148
214
  .where(where);
149
- return this.rethrow(qb.getCount(pks, true));
215
+ return this.rethrow(qb.getCount());
150
216
  }
151
217
  async nativeInsert(entityName, data, options = {}) {
152
218
  options.convertCustomTypes ?? (options.convertCustomTypes = true);
153
219
  const meta = this.metadata.find(entityName);
154
220
  const collections = this.extractManyToMany(entityName, data);
155
- const pks = this.getPrimaryKeyFields(entityName);
156
- const qb = this.createQueryBuilder(entityName, options.ctx, true, options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
221
+ const pks = meta?.primaryKeys ?? [this.config.getNamingStrategy().referenceColumnName()];
222
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
157
223
  const res = await this.rethrow(qb.insert(data).execute('run', false));
158
224
  res.row = res.row || {};
159
225
  let pk;
@@ -161,7 +227,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
161
227
  pk = core_1.Utils.getPrimaryKeyCond(data, pks);
162
228
  }
163
229
  else {
164
- res.insertId = data[pks[0]] || res.insertId || res.row[pks[0]];
230
+ /* istanbul ignore next */
231
+ res.insertId = data[pks[0]] ?? res.insertId ?? res.row[pks[0]];
165
232
  pk = [res.insertId];
166
233
  }
167
234
  await this.processManyToMany(meta, pk, collections, false, options);
@@ -170,29 +237,30 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
170
237
  async nativeInsertMany(entityName, data, options = {}) {
171
238
  options.processCollections ?? (options.processCollections = true);
172
239
  options.convertCustomTypes ?? (options.convertCustomTypes = true);
173
- const meta = this.metadata.get(entityName);
240
+ const meta = this.metadata.find(entityName);
174
241
  const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
175
242
  const pks = this.getPrimaryKeyFields(entityName);
176
243
  const set = new Set();
177
244
  data.forEach(row => Object.keys(row).forEach(k => set.add(k)));
178
- const props = [...set].map(name => meta.properties[name] ?? { name, fieldNames: [name] });
245
+ const props = [...set].map(name => meta?.properties[name] ?? { name, fieldNames: [name] });
179
246
  const fields = core_1.Utils.flatten(props.map(prop => prop.fieldNames));
180
- let res;
181
- if (fields.length === 0) {
182
- const qb = this.createQueryBuilder(entityName, options.ctx, true, options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
183
- res = await this.rethrow(qb.insert(data).execute('run', false));
247
+ const params = [];
248
+ /* istanbul ignore next */
249
+ const tableName = meta ? this.getTableName(meta, options) : this.platform.quoteIdentifier(entityName);
250
+ let sql = `insert into ${tableName} `;
251
+ sql += fields.length > 0 ? '(' + fields.map(k => this.platform.quoteIdentifier(k)).join(', ') + ')' : `(${this.platform.quoteIdentifier(pks[0])})`;
252
+ if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
253
+ sql += ' values ';
184
254
  }
185
255
  else {
186
- let sql = `insert into ${this.getTableName(meta, options)} `;
187
- /* istanbul ignore next */
188
- sql += fields.length > 0 ? '(' + fields.map(k => this.platform.quoteIdentifier(k)).join(', ') + ')' : 'default';
189
- sql += ` values `;
190
- const params = [];
256
+ sql += ' ' + data.map(() => `select null as ${this.platform.quoteIdentifier(pks[0])}`).join(' union all ');
257
+ }
258
+ if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
191
259
  sql += data.map(row => {
192
260
  const keys = [];
193
261
  props.forEach(prop => {
194
262
  if (prop.fieldNames.length > 1) {
195
- params.push(...row[prop.name]);
263
+ params.push(...(row[prop.name] ?? prop.fieldNames.map(() => null)));
196
264
  keys.push(...prop.fieldNames.map(_ => '?'));
197
265
  }
198
266
  else if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(row[prop.name])) {
@@ -204,17 +272,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
204
272
  keys.push('?');
205
273
  }
206
274
  });
207
- return '(' + keys.join(', ') + ')';
275
+ return '(' + (keys.join(', ') || 'default') + ')';
208
276
  }).join(', ');
209
- if (this.platform.usesReturningStatement()) {
210
- /* istanbul ignore next */
211
- const returningProps = meta.props.filter(prop => prop.primary || prop.defaultRaw);
212
- const returningFields = core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames));
213
- /* istanbul ignore next */
214
- sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
215
- }
216
- res = await this.execute(sql, params, 'run', options.ctx);
217
277
  }
278
+ if (this.platform.usesReturningStatement()) {
279
+ /* istanbul ignore next */
280
+ const returningProps = meta.hydrateProps.filter(prop => prop.persist !== false && (prop.primary || prop.defaultRaw));
281
+ const returningFields = core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames));
282
+ /* istanbul ignore next */
283
+ sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
284
+ }
285
+ const res = await this.execute(sql, params, 'run', options.ctx);
218
286
  let pk;
219
287
  /* istanbul ignore next */
220
288
  if (pks.length > 1) { // owner has composite pk
@@ -238,13 +306,23 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
238
306
  const collections = this.extractManyToMany(entityName, data);
239
307
  let res = { affectedRows: 0, insertId: 0, row: {} };
240
308
  if (core_1.Utils.isPrimaryKey(where) && pks.length === 1) {
241
- where = { [pks[0]]: where };
309
+ /* istanbul ignore next */
310
+ where = { [meta?.primaryKeys[0] ?? pks[0]]: where };
242
311
  }
243
312
  if (core_1.Utils.hasObjectKeys(data)) {
244
- const qb = this.createQueryBuilder(entityName, options.ctx, true, options.convertCustomTypes)
245
- .update(data)
246
- .withSchema(this.getSchemaName(meta, options))
247
- .where(where);
313
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes)
314
+ .withSchema(this.getSchemaName(meta, options));
315
+ if (options.upsert) {
316
+ /* istanbul ignore next */
317
+ const uniqueFields = core_1.Utils.isPlainObject(where) ? Object.keys(where) : meta.primaryKeys;
318
+ /* istanbul ignore next */
319
+ qb.insert(data)
320
+ .onConflict(uniqueFields.map(p => meta?.properties[p]?.fieldNames[0] ?? p))
321
+ .merge(Object.keys(data).filter(f => !uniqueFields.includes(f)));
322
+ }
323
+ else {
324
+ qb.update(data).where(where);
325
+ }
248
326
  res = await this.rethrow(qb.execute('run', false));
249
327
  }
250
328
  /* istanbul ignore next */
@@ -276,7 +354,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
276
354
  else {
277
355
  sql += '?';
278
356
  }
279
- params.push(...pks, prop.fieldNames.length > 1 ? data[idx][key][fieldNameIdx] : data[idx][key]);
357
+ params.push(...pks, prop.fieldNames.length > 1 ? data[idx][key]?.[fieldNameIdx] : data[idx][key]);
280
358
  }
281
359
  });
282
360
  sql += ` else ${this.platform.quoteIdentifier(fieldName)} end, `;
@@ -300,15 +378,24 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
300
378
  const pks = core_1.Utils.flatten(pkProps.map(pk => meta.properties[pk].fieldNames));
301
379
  sql += pks.length > 1 ? `(${pks.map(pk => `${this.platform.quoteIdentifier(pk)}`).join(', ')})` : this.platform.quoteIdentifier(pks[0]);
302
380
  const conds = where.map(cond => {
381
+ if (core_1.Utils.isPlainObject(cond) && core_1.Utils.getObjectKeysSize(cond) === 1) {
382
+ cond = Object.values(cond)[0];
383
+ }
303
384
  if (pks.length > 1) {
304
- pkProps.forEach(pk => params.push(cond[pk]));
385
+ pkProps.forEach(pk => {
386
+ if (Array.isArray(cond[pk])) {
387
+ params.push(...core_1.Utils.flatten(cond[pk]));
388
+ }
389
+ else {
390
+ params.push(cond[pk]);
391
+ }
392
+ });
305
393
  return `(${new Array(pks.length).fill('?').join(', ')})`;
306
394
  }
307
395
  params.push(cond);
308
396
  return '?';
309
397
  });
310
- const values = pks.length > 1 && this.platform.requiresValuesKeyword() ? 'values ' : '';
311
- sql += ` in (${values}${conds.join(', ')})`;
398
+ sql += ` in (${conds.join(', ')})`;
312
399
  const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx));
313
400
  for (let i = 0; i < collections.length; i++) {
314
401
  await this.processManyToMany(meta, where[i], collections[i], false, options);
@@ -321,17 +408,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
321
408
  if (core_1.Utils.isPrimaryKey(where) && pks.length === 1) {
322
409
  where = { [pks[0]]: where };
323
410
  }
324
- const qb = this.createQueryBuilder(entityName, options.ctx, true, false).delete(where).withSchema(this.getSchemaName(meta, options));
411
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false).delete(where).withSchema(this.getSchemaName(meta, options));
325
412
  return this.rethrow(qb.execute('run', false));
326
413
  }
327
414
  async syncCollection(coll, options) {
328
- const wrapped = coll.owner.__helper;
415
+ const wrapped = (0, core_1.helper)(coll.owner);
329
416
  const meta = wrapped.__meta;
330
417
  const pks = wrapped.getPrimaryKeys(true);
331
418
  const snap = coll.getSnapshot();
332
419
  const includes = (arr, item) => !!arr.find(i => core_1.Utils.equals(i, item));
333
- const snapshot = snap ? snap.map(item => item.__helper.getPrimaryKeys(true)) : [];
334
- const current = coll.getItems(false).map(item => item.__helper.getPrimaryKeys(true));
420
+ const snapshot = snap ? snap.map(item => (0, core_1.helper)(item).getPrimaryKeys(true)) : [];
421
+ const current = coll.getItems(false).map(item => (0, core_1.helper)(item).getPrimaryKeys(true));
335
422
  const deleteDiff = snap ? snapshot.filter(item => !includes(current, item)) : true;
336
423
  const insertDiff = current.filter(item => !includes(snapshot, item));
337
424
  const target = snapshot.filter(item => includes(current, item)).concat(...insertDiff);
@@ -345,7 +432,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
345
432
  }
346
433
  if (coll.property.reference === core_1.ReferenceType.ONE_TO_MANY) {
347
434
  const cols = coll.property.referencedColumnNames;
348
- const qb = this.createQueryBuilder(coll.property.type, ctx, true)
435
+ const qb = this.createQueryBuilder(coll.property.type, ctx, 'write')
349
436
  .withSchema(this.getSchemaName(meta, options))
350
437
  .update({ [coll.property.mappedBy]: pks })
351
438
  .getKnexQuery()
@@ -354,7 +441,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
354
441
  }
355
442
  /* istanbul ignore next */
356
443
  const ownerSchema = wrapped.getSchema() === '*' ? this.config.get('schema') : wrapped.getSchema();
357
- const pivotMeta = this.metadata.find(coll.property.pivotTable);
444
+ const pivotMeta = this.metadata.find(coll.property.pivotEntity);
358
445
  if (pivotMeta.schema === '*') {
359
446
  /* istanbul ignore next */
360
447
  options ?? (options = {});
@@ -365,7 +452,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
365
452
  async loadFromPivotTable(prop, owners, where = {}, orderBy, ctx, options) {
366
453
  const pivotProp2 = this.getPivotInverseProperty(prop);
367
454
  const ownerMeta = this.metadata.find(pivotProp2.type);
368
- const cond = { [`${prop.pivotTable}.${pivotProp2.name}`]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) } };
455
+ const pivotMeta = this.metadata.find(prop.pivotEntity);
456
+ const cond = { [`${prop.pivotEntity}.${pivotProp2.name}`]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) } };
369
457
  /* istanbul ignore if */
370
458
  if (!core_1.Utils.isEmpty(where) && Object.keys(where).every(k => core_1.Utils.isOperator(k, false))) {
371
459
  where = cond;
@@ -374,15 +462,23 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
374
462
  where = { ...where, ...cond };
375
463
  }
376
464
  orderBy = this.getPivotOrderBy(prop, orderBy);
377
- const qb = this.createQueryBuilder(prop.type, ctx, !!ctx)
465
+ const qb = this.createQueryBuilder(prop.type, ctx, options?.connectionType)
378
466
  .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES)
379
467
  .withSchema(this.getSchemaName(prop.targetMeta, options));
380
- const populate = this.autoJoinOneToOneOwner(prop.targetMeta, [{ field: prop.pivotTable }]);
468
+ const populate = this.autoJoinOneToOneOwner(prop.targetMeta, [{ field: prop.pivotEntity }]);
381
469
  const fields = this.buildFields(prop.targetMeta, (options?.populate ?? []), [], qb, options?.fields);
382
470
  qb.select(fields).populate(populate).where(where).orderBy(orderBy).setLockMode(options?.lockMode, options?.lockTableAliases);
383
471
  if (owners.length === 1 && (options?.offset != null || options?.limit != null)) {
384
472
  qb.limit(options.limit, options.offset);
385
473
  }
474
+ if (prop.targetMeta.schema !== '*' && pivotMeta.schema === '*' && options?.schema) {
475
+ // eslint-disable-next-line dot-notation
476
+ qb['finalize']();
477
+ // eslint-disable-next-line dot-notation
478
+ Object.values(qb['_joins']).forEach(join => {
479
+ join.schema = options.schema;
480
+ });
481
+ }
386
482
  const items = owners.length ? await this.rethrow(qb.execute('all')) : [];
387
483
  const map = {};
388
484
  const pkProps = ownerMeta.getPrimaryProps();
@@ -407,26 +503,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
407
503
  async execute(queryOrKnex, params = [], method = 'all', ctx) {
408
504
  return this.rethrow(this.connection.execute(queryOrKnex, params, method, ctx));
409
505
  }
410
- getTableName(meta, options) {
411
- let tableName = this.platform.quoteIdentifier(meta.collection);
412
- if (options.schema === '*') {
413
- return this.platform.quoteIdentifier(this.config.get('schema')) + '.' + tableName;
414
- }
415
- if (meta.schema || options.schema) {
416
- tableName = this.platform.quoteIdentifier(options.schema ?? meta.schema) + '.' + tableName;
417
- }
418
- return tableName;
419
- }
420
506
  /**
421
507
  * 1:1 owner side needs to be marked for population so QB auto-joins the owner id
422
508
  */
423
509
  autoJoinOneToOneOwner(meta, populate, fields = []) {
424
- if (!this.config.get('autoJoinOneToOneOwner') || fields.length > 0) {
510
+ if (!this.config.get('autoJoinOneToOneOwner')) {
425
511
  return populate;
426
512
  }
427
513
  const relationsToPopulate = populate.map(({ field }) => field);
428
514
  const toPopulate = meta.relations
429
515
  .filter(prop => prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
516
+ .filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
430
517
  .map(prop => ({ field: prop.name, strategy: prop.strategy }));
431
518
  return [...populate, ...toPopulate];
432
519
  }
@@ -506,13 +593,27 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
506
593
  }
507
594
  return prop.fieldNames;
508
595
  }
509
- createQueryBuilder(entityName, ctx, write, convertCustomTypes) {
510
- const qb = new QueryBuilder_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, write ? 'write' : 'read');
596
+ /** @internal */
597
+ createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes) {
598
+ const connectionType = this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
599
+ const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType);
511
600
  if (!convertCustomTypes) {
512
601
  qb.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
513
602
  }
514
603
  return qb;
515
604
  }
605
+ resolveConnectionType(args) {
606
+ if (args.ctx) {
607
+ return 'write';
608
+ }
609
+ else if (args.connectionType) {
610
+ return args.connectionType;
611
+ }
612
+ else if (this.config.get('preferReadReplicas') === true) {
613
+ return 'read';
614
+ }
615
+ return 'write';
616
+ }
516
617
  extractManyToMany(entityName, data) {
517
618
  if (!this.metadata.has(entityName)) {
518
619
  return {};
@@ -541,7 +642,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
541
642
  deleteDiff = [];
542
643
  }
543
644
  if (deleteDiff === true || deleteDiff.length > 0) {
544
- const qb1 = this.createQueryBuilder(prop.pivotTable, options?.ctx, true)
645
+ const qb1 = this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
545
646
  .withSchema(this.getSchemaName(meta, options))
546
647
  .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
547
648
  const knex = qb1.getKnex();
@@ -562,11 +663,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
562
663
  });
563
664
  /* istanbul ignore else */
564
665
  if (this.platform.allowsMultiInsert()) {
565
- await this.nativeInsertMany(prop.pivotTable, items, { ...options, convertCustomTypes: false, processCollections: false });
666
+ await this.nativeInsertMany(prop.pivotEntity, items, { ...options, convertCustomTypes: false, processCollections: false });
566
667
  }
567
668
  else {
568
669
  await core_1.Utils.runSerial(items, item => {
569
- return this.createQueryBuilder(prop.pivotTable, options?.ctx, true)
670
+ return this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
570
671
  .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES)
571
672
  .withSchema(this.getSchemaName(meta, options))
572
673
  .insert(item)
@@ -575,7 +676,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
575
676
  }
576
677
  }
577
678
  async lockPessimistic(entity, options) {
578
- const meta = entity.__helper.__meta;
679
+ const meta = (0, core_1.helper)(entity).__meta;
579
680
  const qb = this.createQueryBuilder(entity.constructor.name, options.ctx).unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES).withSchema(options.schema ?? meta.schema);
580
681
  const cond = core_1.Utils.getPrimaryKeyCond(entity, meta.primaryKeys);
581
682
  qb.select('1').where(cond).setLockMode(options.lockMode, options.lockTableAliases);
@@ -601,6 +702,43 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
601
702
  });
602
703
  return orderBy;
603
704
  }
705
+ normalizeFields(fields, prefix = '') {
706
+ const ret = [];
707
+ for (const field of fields) {
708
+ if (typeof field === 'string') {
709
+ ret.push(prefix + field);
710
+ continue;
711
+ }
712
+ if (core_1.Utils.isPlainObject(field)) {
713
+ for (const key of Object.keys(field)) {
714
+ ret.push(...this.normalizeFields(field[key], key + '.'));
715
+ }
716
+ }
717
+ }
718
+ return ret;
719
+ }
720
+ processField(meta, prop, field, ret, populate, joinedProps, qb) {
721
+ if (!prop || (prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner)) {
722
+ return;
723
+ }
724
+ if (prop.reference === core_1.ReferenceType.EMBEDDED) {
725
+ if (prop.object) {
726
+ ret.push(prop.fieldNames[0]);
727
+ return;
728
+ }
729
+ const parts = field.split('.');
730
+ const top = parts.shift();
731
+ for (const key of Object.keys(prop.embeddedProps)) {
732
+ if (!top || key === top) {
733
+ this.processField(meta, prop.embeddedProps[key], parts.join('.'), ret, populate, joinedProps, qb);
734
+ }
735
+ }
736
+ return;
737
+ }
738
+ if (prop.fieldNames) {
739
+ ret.push(prop.fieldNames[0]);
740
+ }
741
+ }
604
742
  buildFields(meta, populate, joinedProps, qb, fields) {
605
743
  const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => p.field === prop.name || p.all));
606
744
  const hasLazyFormulas = meta.props.some(p => p.lazy && p.formula);
@@ -611,11 +749,21 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
611
749
  ret.push(...this.getFieldsForJoinedLoad(qb, meta, fields, populate));
612
750
  }
613
751
  else if (fields) {
614
- for (const field of [...fields]) {
615
- if (core_1.Utils.isPlainObject(field) || field.toString().includes('.')) {
752
+ for (const field of this.normalizeFields(fields)) {
753
+ if (field === '*') {
754
+ ret.push('*');
616
755
  continue;
617
756
  }
618
- ret.push(field);
757
+ const parts = field.split('.');
758
+ const rootPropName = parts.shift(); // first one is the `prop`
759
+ const prop = core_1.QueryHelper.findProperty(rootPropName, {
760
+ metadata: this.metadata,
761
+ platform: this.platform,
762
+ entityName: meta.className,
763
+ where: {},
764
+ aliasMap: qb.getAliasMap(),
765
+ });
766
+ this.processField(meta, prop, parts.join('.'), ret, populate, joinedProps, qb);
619
767
  }
620
768
  ret.unshift(...meta.primaryKeys.filter(pk => !fields.includes(pk)));
621
769
  }
@@ -638,7 +786,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
638
786
  .filter(prop => prop.customType?.convertToDatabaseValueSQL || prop.customType?.convertToJSValueSQL)
639
787
  .forEach(prop => ret.push(prop.name));
640
788
  }
641
- return ret.length > 0 ? ret : ['*'];
789
+ return ret.length > 0 ? core_1.Utils.unique(ret) : ['*'];
642
790
  }
643
791
  }
644
792
  exports.AbstractSqlDriver = AbstractSqlDriver;
@@ -1,4 +1,4 @@
1
- import type { Constructor, EntityManager, EntityRepository, IDatabaseDriver } from '@mikro-orm/core';
1
+ import type { Constructor, EntityManager, EntityRepository, IDatabaseDriver, MikroORM } from '@mikro-orm/core';
2
2
  import { Platform } from '@mikro-orm/core';
3
3
  import type { SchemaHelper } from './schema';
4
4
  import { SchemaGenerator } from './schema';
@@ -6,14 +6,14 @@ export declare abstract class AbstractSqlPlatform extends Platform {
6
6
  protected readonly schemaHelper?: SchemaHelper;
7
7
  usesPivotTable(): boolean;
8
8
  indexForeignKeys(): boolean;
9
- getRepositoryClass<T>(): Constructor<EntityRepository<T>>;
9
+ getRepositoryClass<T extends object>(): Constructor<EntityRepository<T>>;
10
10
  getSchemaHelper(): SchemaHelper | undefined;
11
- getSchemaGenerator(driver: IDatabaseDriver): SchemaGenerator;
12
- getEntityGenerator(em: EntityManager): any;
13
- getMigrator(em: EntityManager): any;
11
+ /** @inheritDoc */
12
+ lookupExtensions(orm: MikroORM): void;
13
+ getSchemaGenerator(driver: IDatabaseDriver, em?: EntityManager): SchemaGenerator;
14
14
  quoteValue(value: any): string;
15
15
  formatQuery(sql: string, params: readonly any[]): string;
16
- getSearchJsonPropertySQL(path: string, type: string): string;
16
+ getSearchJsonPropertySQL(path: string, type: string, aliased: boolean): string;
17
17
  isRaw(value: any): boolean;
18
18
  supportsSchemas(): boolean;
19
19
  /** @inheritDoc */