@mikro-orm/knex 6.0.0-dev.2 → 6.0.0-dev.21

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 (54) 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} +230 -71
  5. package/{dist/AbstractSqlPlatform.d.ts → AbstractSqlPlatform.d.ts} +7 -7
  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} +1 -2
  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} +1 -0
  15. package/{dist/index.js → index.js} +6 -1
  16. package/index.mjs +192 -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 +65 -24
  27. package/{dist/query → query}/QueryBuilder.js +318 -84
  28. package/{dist/query → query}/QueryBuilderHelper.d.ts +19 -11
  29. package/{dist/query → query}/QueryBuilderHelper.js +224 -113
  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 +8 -8
  39. package/{dist/schema → schema}/DatabaseTable.js +2 -1
  40. package/{dist/schema → schema}/SchemaComparator.d.ts +7 -5
  41. package/{dist/schema → schema}/SchemaComparator.js +60 -10
  42. package/{dist/schema → schema}/SchemaHelper.d.ts +15 -10
  43. package/{dist/schema → schema}/SchemaHelper.js +46 -8
  44. package/{dist/schema/SchemaGenerator.d.ts → schema/SqlSchemaGenerator.d.ts} +12 -12
  45. package/{dist/schema/SchemaGenerator.js → schema/SqlSchemaGenerator.js} +85 -34
  46. package/{dist/schema → schema}/index.d.ts +1 -1
  47. package/{dist/schema → schema}/index.js +6 -2
  48. package/{dist/typings.d.ts → typings.d.ts} +16 -12
  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/index.mjs +0 -26
  54. 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<Connection extends AbstractSqlConnection = AbstractSqlConnection, Platform extends AbstractSqlPlatform = AbstractSqlPlatform> extends DatabaseDriver<Connection> {
10
+ [EntityManagerType]: SqlEntityManager<this>;
11
+ protected readonly connection: Connection;
12
+ protected readonly replicas: Connection[];
13
+ protected readonly platform: Platform;
14
+ protected constructor(config: Configuration, platform: Platform, connection: Constructor<Connection>, connector: string[]);
15
+ getPlatform(): Platform;
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 */
@@ -256,6 +334,14 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
256
334
  options.processCollections ?? (options.processCollections = true);
257
335
  options.convertCustomTypes ?? (options.convertCustomTypes = true);
258
336
  const meta = this.metadata.get(entityName);
337
+ if (options.upsert) {
338
+ const uniqueFields = core_1.Utils.isPlainObject(where[0]) ? Object.keys(where[0]) : meta.primaryKeys;
339
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
340
+ qb.insert(data)
341
+ .onConflict(uniqueFields.map(p => meta?.properties[p]?.fieldNames[0] ?? p))
342
+ .merge(Object.keys(data[0]).filter(f => !uniqueFields.includes(f)));
343
+ return qb.execute('run', false);
344
+ }
259
345
  const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
260
346
  const keys = new Set();
261
347
  data.forEach(row => Object.keys(row).forEach(k => keys.add(k)));
@@ -276,7 +362,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
276
362
  else {
277
363
  sql += '?';
278
364
  }
279
- params.push(...pks, prop.fieldNames.length > 1 ? data[idx][key][fieldNameIdx] : data[idx][key]);
365
+ params.push(...pks, prop.fieldNames.length > 1 ? data[idx][key]?.[fieldNameIdx] : data[idx][key]);
280
366
  }
281
367
  });
282
368
  sql += ` else ${this.platform.quoteIdentifier(fieldName)} end, `;
@@ -300,15 +386,24 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
300
386
  const pks = core_1.Utils.flatten(pkProps.map(pk => meta.properties[pk].fieldNames));
301
387
  sql += pks.length > 1 ? `(${pks.map(pk => `${this.platform.quoteIdentifier(pk)}`).join(', ')})` : this.platform.quoteIdentifier(pks[0]);
302
388
  const conds = where.map(cond => {
389
+ if (core_1.Utils.isPlainObject(cond) && core_1.Utils.getObjectKeysSize(cond) === 1) {
390
+ cond = Object.values(cond)[0];
391
+ }
303
392
  if (pks.length > 1) {
304
- pkProps.forEach(pk => params.push(cond[pk]));
393
+ pkProps.forEach(pk => {
394
+ if (Array.isArray(cond[pk])) {
395
+ params.push(...core_1.Utils.flatten(cond[pk]));
396
+ }
397
+ else {
398
+ params.push(cond[pk]);
399
+ }
400
+ });
305
401
  return `(${new Array(pks.length).fill('?').join(', ')})`;
306
402
  }
307
403
  params.push(cond);
308
404
  return '?';
309
405
  });
310
- const values = pks.length > 1 && this.platform.requiresValuesKeyword() ? 'values ' : '';
311
- sql += ` in (${values}${conds.join(', ')})`;
406
+ sql += ` in (${conds.join(', ')})`;
312
407
  const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx));
313
408
  for (let i = 0; i < collections.length; i++) {
314
409
  await this.processManyToMany(meta, where[i], collections[i], false, options);
@@ -321,17 +416,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
321
416
  if (core_1.Utils.isPrimaryKey(where) && pks.length === 1) {
322
417
  where = { [pks[0]]: where };
323
418
  }
324
- const qb = this.createQueryBuilder(entityName, options.ctx, true, false).delete(where).withSchema(this.getSchemaName(meta, options));
419
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false).delete(where).withSchema(this.getSchemaName(meta, options));
325
420
  return this.rethrow(qb.execute('run', false));
326
421
  }
327
422
  async syncCollection(coll, options) {
328
- const wrapped = coll.owner.__helper;
423
+ const wrapped = (0, core_1.helper)(coll.owner);
329
424
  const meta = wrapped.__meta;
330
425
  const pks = wrapped.getPrimaryKeys(true);
331
426
  const snap = coll.getSnapshot();
332
427
  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));
428
+ const snapshot = snap ? snap.map(item => (0, core_1.helper)(item).getPrimaryKeys(true)) : [];
429
+ const current = coll.getItems(false).map(item => (0, core_1.helper)(item).getPrimaryKeys(true));
335
430
  const deleteDiff = snap ? snapshot.filter(item => !includes(current, item)) : true;
336
431
  const insertDiff = current.filter(item => !includes(snapshot, item));
337
432
  const target = snapshot.filter(item => includes(current, item)).concat(...insertDiff);
@@ -345,7 +440,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
345
440
  }
346
441
  if (coll.property.reference === core_1.ReferenceType.ONE_TO_MANY) {
347
442
  const cols = coll.property.referencedColumnNames;
348
- const qb = this.createQueryBuilder(coll.property.type, ctx, true)
443
+ const qb = this.createQueryBuilder(coll.property.type, ctx, 'write')
349
444
  .withSchema(this.getSchemaName(meta, options))
350
445
  .update({ [coll.property.mappedBy]: pks })
351
446
  .getKnexQuery()
@@ -354,7 +449,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
354
449
  }
355
450
  /* istanbul ignore next */
356
451
  const ownerSchema = wrapped.getSchema() === '*' ? this.config.get('schema') : wrapped.getSchema();
357
- const pivotMeta = this.metadata.find(coll.property.pivotTable);
452
+ const pivotMeta = this.metadata.find(coll.property.pivotEntity);
358
453
  if (pivotMeta.schema === '*') {
359
454
  /* istanbul ignore next */
360
455
  options ?? (options = {});
@@ -365,7 +460,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
365
460
  async loadFromPivotTable(prop, owners, where = {}, orderBy, ctx, options) {
366
461
  const pivotProp2 = this.getPivotInverseProperty(prop);
367
462
  const ownerMeta = this.metadata.find(pivotProp2.type);
368
- const cond = { [`${prop.pivotTable}.${pivotProp2.name}`]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) } };
463
+ const pivotMeta = this.metadata.find(prop.pivotEntity);
464
+ const cond = { [`${prop.pivotEntity}.${pivotProp2.name}`]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) } };
369
465
  /* istanbul ignore if */
370
466
  if (!core_1.Utils.isEmpty(where) && Object.keys(where).every(k => core_1.Utils.isOperator(k, false))) {
371
467
  where = cond;
@@ -374,15 +470,23 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
374
470
  where = { ...where, ...cond };
375
471
  }
376
472
  orderBy = this.getPivotOrderBy(prop, orderBy);
377
- const qb = this.createQueryBuilder(prop.type, ctx, !!ctx)
473
+ const qb = this.createQueryBuilder(prop.type, ctx, options?.connectionType)
378
474
  .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES)
379
475
  .withSchema(this.getSchemaName(prop.targetMeta, options));
380
- const populate = this.autoJoinOneToOneOwner(prop.targetMeta, [{ field: prop.pivotTable }]);
476
+ const populate = this.autoJoinOneToOneOwner(prop.targetMeta, [{ field: prop.pivotEntity }]);
381
477
  const fields = this.buildFields(prop.targetMeta, (options?.populate ?? []), [], qb, options?.fields);
382
478
  qb.select(fields).populate(populate).where(where).orderBy(orderBy).setLockMode(options?.lockMode, options?.lockTableAliases);
383
479
  if (owners.length === 1 && (options?.offset != null || options?.limit != null)) {
384
480
  qb.limit(options.limit, options.offset);
385
481
  }
482
+ if (prop.targetMeta.schema !== '*' && pivotMeta.schema === '*' && options?.schema) {
483
+ // eslint-disable-next-line dot-notation
484
+ qb['finalize']();
485
+ // eslint-disable-next-line dot-notation
486
+ Object.values(qb['_joins']).forEach(join => {
487
+ join.schema = options.schema;
488
+ });
489
+ }
386
490
  const items = owners.length ? await this.rethrow(qb.execute('all')) : [];
387
491
  const map = {};
388
492
  const pkProps = ownerMeta.getPrimaryProps();
@@ -407,26 +511,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
407
511
  async execute(queryOrKnex, params = [], method = 'all', ctx) {
408
512
  return this.rethrow(this.connection.execute(queryOrKnex, params, method, ctx));
409
513
  }
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
514
  /**
421
515
  * 1:1 owner side needs to be marked for population so QB auto-joins the owner id
422
516
  */
423
517
  autoJoinOneToOneOwner(meta, populate, fields = []) {
424
- if (!this.config.get('autoJoinOneToOneOwner') || fields.length > 0) {
518
+ if (!this.config.get('autoJoinOneToOneOwner')) {
425
519
  return populate;
426
520
  }
427
521
  const relationsToPopulate = populate.map(({ field }) => field);
428
522
  const toPopulate = meta.relations
429
523
  .filter(prop => prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
524
+ .filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
430
525
  .map(prop => ({ field: prop.name, strategy: prop.strategy }));
431
526
  return [...populate, ...toPopulate];
432
527
  }
@@ -458,12 +553,15 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
458
553
  getFieldsForJoinedLoad(qb, meta, explicitFields, populate = [], parentTableAlias, parentJoinPath) {
459
554
  const fields = [];
460
555
  const joinedProps = this.joinedProps(meta, populate);
556
+ if (explicitFields?.includes('*')) {
557
+ fields.push('*');
558
+ }
461
559
  const shouldHaveColumn = (prop, populate, fields) => {
462
560
  if (!this.platform.shouldHaveColumn(prop, populate)) {
463
561
  return false;
464
562
  }
465
563
  if (!fields || prop.primary) {
466
- return true;
564
+ return !fields?.includes('*');
467
565
  }
468
566
  return fields.includes(prop.name);
469
567
  };
@@ -506,13 +604,27 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
506
604
  }
507
605
  return prop.fieldNames;
508
606
  }
509
- createQueryBuilder(entityName, ctx, write, convertCustomTypes) {
510
- const qb = new QueryBuilder_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, write ? 'write' : 'read');
607
+ /** @internal */
608
+ createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes) {
609
+ const connectionType = this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
610
+ const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType);
511
611
  if (!convertCustomTypes) {
512
612
  qb.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
513
613
  }
514
614
  return qb;
515
615
  }
616
+ resolveConnectionType(args) {
617
+ if (args.ctx) {
618
+ return 'write';
619
+ }
620
+ else if (args.connectionType) {
621
+ return args.connectionType;
622
+ }
623
+ else if (this.config.get('preferReadReplicas') === true) {
624
+ return 'read';
625
+ }
626
+ return 'write';
627
+ }
516
628
  extractManyToMany(entityName, data) {
517
629
  if (!this.metadata.has(entityName)) {
518
630
  return {};
@@ -541,7 +653,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
541
653
  deleteDiff = [];
542
654
  }
543
655
  if (deleteDiff === true || deleteDiff.length > 0) {
544
- const qb1 = this.createQueryBuilder(prop.pivotTable, options?.ctx, true)
656
+ const qb1 = this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
545
657
  .withSchema(this.getSchemaName(meta, options))
546
658
  .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
547
659
  const knex = qb1.getKnex();
@@ -562,11 +674,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
562
674
  });
563
675
  /* istanbul ignore else */
564
676
  if (this.platform.allowsMultiInsert()) {
565
- await this.nativeInsertMany(prop.pivotTable, items, { ...options, convertCustomTypes: false, processCollections: false });
677
+ await this.nativeInsertMany(prop.pivotEntity, items, { ...options, convertCustomTypes: false, processCollections: false });
566
678
  }
567
679
  else {
568
680
  await core_1.Utils.runSerial(items, item => {
569
- return this.createQueryBuilder(prop.pivotTable, options?.ctx, true)
681
+ return this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
570
682
  .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES)
571
683
  .withSchema(this.getSchemaName(meta, options))
572
684
  .insert(item)
@@ -575,7 +687,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
575
687
  }
576
688
  }
577
689
  async lockPessimistic(entity, options) {
578
- const meta = entity.__helper.__meta;
690
+ const meta = (0, core_1.helper)(entity).__meta;
579
691
  const qb = this.createQueryBuilder(entity.constructor.name, options.ctx).unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES).withSchema(options.schema ?? meta.schema);
580
692
  const cond = core_1.Utils.getPrimaryKeyCond(entity, meta.primaryKeys);
581
693
  qb.select('1').where(cond).setLockMode(options.lockMode, options.lockTableAliases);
@@ -601,6 +713,43 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
601
713
  });
602
714
  return orderBy;
603
715
  }
716
+ normalizeFields(fields, prefix = '') {
717
+ const ret = [];
718
+ for (const field of fields) {
719
+ if (typeof field === 'string') {
720
+ ret.push(prefix + field);
721
+ continue;
722
+ }
723
+ if (core_1.Utils.isPlainObject(field)) {
724
+ for (const key of Object.keys(field)) {
725
+ ret.push(...this.normalizeFields(field[key], key + '.'));
726
+ }
727
+ }
728
+ }
729
+ return ret;
730
+ }
731
+ processField(meta, prop, field, ret, populate, joinedProps, qb) {
732
+ if (!prop || (prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner)) {
733
+ return;
734
+ }
735
+ if (prop.reference === core_1.ReferenceType.EMBEDDED) {
736
+ if (prop.object) {
737
+ ret.push(prop.fieldNames[0]);
738
+ return;
739
+ }
740
+ const parts = field.split('.');
741
+ const top = parts.shift();
742
+ for (const key of Object.keys(prop.embeddedProps)) {
743
+ if (!top || key === top) {
744
+ this.processField(meta, prop.embeddedProps[key], parts.join('.'), ret, populate, joinedProps, qb);
745
+ }
746
+ }
747
+ return;
748
+ }
749
+ if (prop.fieldNames) {
750
+ ret.push(prop.fieldNames[0]);
751
+ }
752
+ }
604
753
  buildFields(meta, populate, joinedProps, qb, fields) {
605
754
  const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => p.field === prop.name || p.all));
606
755
  const hasLazyFormulas = meta.props.some(p => p.lazy && p.formula);
@@ -611,11 +760,21 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
611
760
  ret.push(...this.getFieldsForJoinedLoad(qb, meta, fields, populate));
612
761
  }
613
762
  else if (fields) {
614
- for (const field of [...fields]) {
615
- if (core_1.Utils.isPlainObject(field) || field.toString().includes('.')) {
763
+ for (const field of this.normalizeFields(fields)) {
764
+ if (field === '*') {
765
+ ret.push('*');
616
766
  continue;
617
767
  }
618
- ret.push(field);
768
+ const parts = field.split('.');
769
+ const rootPropName = parts.shift(); // first one is the `prop`
770
+ const prop = core_1.QueryHelper.findProperty(rootPropName, {
771
+ metadata: this.metadata,
772
+ platform: this.platform,
773
+ entityName: meta.className,
774
+ where: {},
775
+ aliasMap: qb.getAliasMap(),
776
+ });
777
+ this.processField(meta, prop, parts.join('.'), ret, populate, joinedProps, qb);
619
778
  }
620
779
  ret.unshift(...meta.primaryKeys.filter(pk => !fields.includes(pk)));
621
780
  }
@@ -638,7 +797,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
638
797
  .filter(prop => prop.customType?.convertToDatabaseValueSQL || prop.customType?.convertToJSValueSQL)
639
798
  .forEach(prop => ret.push(prop.name));
640
799
  }
641
- return ret.length > 0 ? ret : ['*'];
800
+ return ret.length > 0 ? core_1.Utils.unique(ret) : ['*'];
642
801
  }
643
802
  }
644
803
  exports.AbstractSqlDriver = AbstractSqlDriver;