@mikro-orm/knex 6.0.0-dev.1 → 6.0.0-dev.100

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 (60) hide show
  1. package/{dist/AbstractSqlConnection.d.ts → AbstractSqlConnection.d.ts} +8 -4
  2. package/{dist/AbstractSqlConnection.js → AbstractSqlConnection.js} +35 -18
  3. package/AbstractSqlDriver.d.ts +66 -0
  4. package/{dist/AbstractSqlDriver.js → AbstractSqlDriver.js} +422 -129
  5. package/AbstractSqlPlatform.d.ts +22 -0
  6. package/{dist/AbstractSqlPlatform.js → AbstractSqlPlatform.js} +46 -19
  7. package/{dist/MonkeyPatchable.d.ts → MonkeyPatchable.d.ts} +1 -0
  8. package/{dist/MonkeyPatchable.js → MonkeyPatchable.js} +3 -0
  9. package/{dist/README.md → README.md} +66 -109
  10. package/{dist/SqlEntityManager.d.ts → SqlEntityManager.d.ts} +6 -10
  11. package/{dist/SqlEntityManager.js → SqlEntityManager.js} +5 -10
  12. package/SqlEntityRepository.d.ts +24 -0
  13. package/{dist/SqlEntityRepository.js → SqlEntityRepository.js} +11 -8
  14. package/{dist/index.d.ts → index.d.ts} +1 -0
  15. package/{dist/index.js → index.js} +6 -1
  16. package/index.mjs +208 -0
  17. package/package.json +20 -49
  18. package/query/ArrayCriteriaNode.d.ts +9 -0
  19. package/{dist/query → query}/CriteriaNode.d.ts +8 -8
  20. package/{dist/query → query}/CriteriaNode.js +23 -13
  21. package/query/CriteriaNodeFactory.d.ts +12 -0
  22. package/{dist/query → query}/CriteriaNodeFactory.js +10 -13
  23. package/{dist/query → query}/ObjectCriteriaNode.d.ts +3 -3
  24. package/{dist/query → query}/ObjectCriteriaNode.js +26 -9
  25. package/{dist/query → query}/QueryBuilder.d.ts +90 -34
  26. package/query/QueryBuilder.js +1098 -0
  27. package/{dist/query → query}/QueryBuilderHelper.d.ts +23 -19
  28. package/{dist/query → query}/QueryBuilderHelper.js +300 -176
  29. package/query/ScalarCriteriaNode.d.ts +10 -0
  30. package/{dist/query → query}/ScalarCriteriaNode.js +9 -6
  31. package/{dist/query → query}/enums.js +1 -1
  32. package/{dist/query → query}/index.js +5 -1
  33. package/{dist/schema → schema}/DatabaseSchema.d.ts +6 -3
  34. package/{dist/schema → schema}/DatabaseSchema.js +33 -23
  35. package/{dist/schema → schema}/DatabaseTable.d.ts +12 -10
  36. package/{dist/schema → schema}/DatabaseTable.js +55 -29
  37. package/{dist/schema → schema}/SchemaComparator.d.ts +7 -5
  38. package/{dist/schema → schema}/SchemaComparator.js +68 -10
  39. package/{dist/schema → schema}/SchemaHelper.d.ts +20 -13
  40. package/{dist/schema → schema}/SchemaHelper.js +56 -11
  41. package/{dist/schema/SchemaGenerator.d.ts → schema/SqlSchemaGenerator.d.ts} +11 -14
  42. package/{dist/schema/SchemaGenerator.js → schema/SqlSchemaGenerator.js} +139 -55
  43. package/{dist/schema → schema}/index.d.ts +1 -1
  44. package/{dist/schema → schema}/index.js +6 -2
  45. package/{dist/typings.d.ts → typings.d.ts} +27 -18
  46. package/CHANGELOG.md +0 -661
  47. package/dist/AbstractSqlDriver.d.ts +0 -55
  48. package/dist/AbstractSqlPlatform.d.ts +0 -21
  49. package/dist/LICENSE +0 -21
  50. package/dist/SqlEntityRepository.d.ts +0 -23
  51. package/dist/index.mjs +0 -26
  52. package/dist/package.json +0 -99
  53. package/dist/query/ArrayCriteriaNode.d.ts +0 -9
  54. package/dist/query/CriteriaNodeFactory.d.ts +0 -12
  55. package/dist/query/QueryBuilder.js +0 -688
  56. package/dist/query/ScalarCriteriaNode.d.ts +0 -9
  57. /package/{dist/query → query}/ArrayCriteriaNode.js +0 -0
  58. /package/{dist/query → query}/enums.d.ts +0 -0
  59. /package/{dist/query → query}/index.d.ts +0 -0
  60. /package/{dist/typings.js → typings.js} +0 -0
@@ -2,12 +2,15 @@
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
+ [core_1.EntityManagerType];
9
+ connection;
10
+ replicas = [];
11
+ platform;
8
12
  constructor(config, platform, connection, connector) {
9
13
  super(config, connector);
10
- this.replicas = [];
11
14
  this.connection = new connection(this.config);
12
15
  this.replicas = this.createReplicas(conf => new connection(this.config, conf, 'read'));
13
16
  this.platform = platform;
@@ -21,21 +24,36 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
21
24
  async find(entityName, where, options = {}) {
22
25
  options = { populate: [], orderBy: [], ...options };
23
26
  const meta = this.metadata.find(entityName);
27
+ if (meta?.virtual) {
28
+ return this.findVirtual(entityName, where, options);
29
+ }
24
30
  const populate = this.autoJoinOneToOneOwner(meta, options.populate, options.fields);
25
31
  const joinedProps = this.joinedProps(meta, populate);
26
- const qb = this.createQueryBuilder(entityName, options.ctx, !!options.ctx, false);
32
+ const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging);
27
33
  const fields = this.buildFields(meta, populate, joinedProps, qb, options.fields);
28
34
  const joinedPropsOrderBy = this.buildJoinedPropsOrderBy(entityName, qb, meta, joinedProps);
35
+ const orderBy = [...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy];
29
36
  if (core_1.Utils.isPrimaryKey(where, meta.compositePK)) {
30
37
  where = { [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
31
38
  }
39
+ const { first, last, before, after } = options;
40
+ const isCursorPagination = [first, last, before, after].some(v => v != null);
32
41
  qb.select(fields)
33
- .populate(populate)
42
+ .populate(populate, joinedProps.length > 0 ? options.populateWhere : undefined)
34
43
  .where(where)
35
- .orderBy([...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy])
36
44
  .groupBy(options.groupBy)
37
45
  .having(options.having)
46
+ .indexHint(options.indexHint)
47
+ .comment(options.comments)
48
+ .hintComment(options.hintComments)
38
49
  .withSchema(this.getSchemaName(meta, options));
50
+ if (isCursorPagination) {
51
+ const { orderBy: newOrderBy, where } = this.processCursorOptions(meta, options, orderBy);
52
+ qb.andWhere(where).orderBy(newOrderBy);
53
+ }
54
+ else {
55
+ qb.orderBy(orderBy);
56
+ }
39
57
  if (options.limit !== undefined) {
40
58
  qb.limit(options.limit, options.offset);
41
59
  }
@@ -44,8 +62,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
44
62
  }
45
63
  core_1.Utils.asArray(options.flags).forEach(flag => qb.setFlag(flag));
46
64
  const result = await this.rethrow(qb.execute('all'));
65
+ if (isCursorPagination && !first && !!last) {
66
+ result.reverse();
67
+ }
47
68
  if (joinedProps.length > 0) {
48
- return this.mergeJoinedResult(result, meta);
69
+ return this.mergeJoinedResult(result, meta, joinedProps);
49
70
  }
50
71
  return result;
51
72
  }
@@ -57,9 +78,73 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
57
78
  if (joinedProps.length === 0) {
58
79
  opts.limit = 1;
59
80
  }
81
+ if (opts.limit > 0 && !opts.flags?.includes(core_1.QueryFlag.DISABLE_PAGINATE)) {
82
+ opts.flags ??= [];
83
+ opts.flags.push(core_1.QueryFlag.DISABLE_PAGINATE);
84
+ }
60
85
  const res = await this.find(entityName, where, opts);
61
86
  return res[0] || null;
62
87
  }
88
+ async findVirtual(entityName, where, options) {
89
+ const meta = this.metadata.get(entityName);
90
+ /* istanbul ignore next */
91
+ if (!meta.expression) {
92
+ return [];
93
+ }
94
+ if (typeof meta.expression === 'string') {
95
+ return this.wrapVirtualExpressionInSubquery(meta, meta.expression, where, options);
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);
102
+ }
103
+ /* istanbul ignore next */
104
+ return res;
105
+ }
106
+ async countVirtual(entityName, where, options) {
107
+ const meta = this.metadata.get(entityName);
108
+ /* istanbul ignore next */
109
+ if (!meta.expression) {
110
+ return 0;
111
+ }
112
+ if (typeof meta.expression === 'string') {
113
+ return this.wrapVirtualExpressionInSubquery(meta, meta.expression, where, options, query_1.QueryType.COUNT);
114
+ }
115
+ const em = this.createEntityManager(false);
116
+ em.setTransactionContext(options.ctx);
117
+ const res = meta.expression(em, where, options);
118
+ if (res instanceof query_1.QueryBuilder) {
119
+ return this.wrapVirtualExpressionInSubquery(meta, res.getFormattedQuery(), where, options, query_1.QueryType.COUNT);
120
+ }
121
+ /* istanbul ignore next */
122
+ return res;
123
+ }
124
+ async wrapVirtualExpressionInSubquery(meta, expression, where, options, type = query_1.QueryType.SELECT) {
125
+ const qb = this.createQueryBuilder(meta.className, options?.ctx, options.connectionType, options.convertCustomTypes)
126
+ .limit(options?.limit, options?.offset)
127
+ .indexHint(options.indexHint)
128
+ .comment(options.comments)
129
+ .hintComment(options.hintComments);
130
+ if (options.orderBy) {
131
+ qb.orderBy(options.orderBy);
132
+ }
133
+ qb.where(where);
134
+ const kqb = qb.getKnexQuery().clear('select');
135
+ if (type === query_1.QueryType.COUNT) {
136
+ kqb.select(this.connection.getKnex().raw('count(*) as count'));
137
+ }
138
+ else { // select
139
+ kqb.select('*');
140
+ }
141
+ kqb.fromRaw(`(${expression}) as ${this.platform.quoteIdentifier(qb.alias)}`);
142
+ const res = await this.execute(kqb);
143
+ if (type === query_1.QueryType.COUNT) {
144
+ return res[0].count;
145
+ }
146
+ return res.map(row => this.mapResult(row, meta));
147
+ }
63
148
  mapResult(result, meta, populate = [], qb, map = {}) {
64
149
  const ret = super.mapResult(result, meta);
65
150
  /* istanbul ignore if */
@@ -90,11 +175,12 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
90
175
  }));
91
176
  if (!hasPK) {
92
177
  // initialize empty collections
93
- if ([core_1.ReferenceType.MANY_TO_MANY, core_1.ReferenceType.ONE_TO_MANY].includes(relation.reference)) {
94
- result[relation.name] = result[relation.name] || [];
178
+ if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
179
+ result[relation.name] ??= [];
95
180
  }
96
181
  return;
97
182
  }
183
+ const tz = this.platform.getTimezone();
98
184
  meta2.props
99
185
  .filter(prop => this.platform.shouldHaveColumn(prop, p.children || []))
100
186
  .forEach(prop => {
@@ -102,6 +188,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
102
188
  relationPojo[prop.name] = prop.fieldNames.map(name => root[`${relationAlias}__${name}`]);
103
189
  prop.fieldNames.map(name => delete root[`${relationAlias}__${name}`]);
104
190
  }
191
+ else if (prop.runtimeType === 'Date') {
192
+ const alias = `${relationAlias}__${prop.fieldNames[0]}`;
193
+ relationPojo[prop.name] = (typeof root[alias] === 'string' ? new Date(root[alias]) : root[alias]);
194
+ delete root[alias];
195
+ }
105
196
  else {
106
197
  const alias = `${relationAlias}__${prop.fieldNames[0]}`;
107
198
  relationPojo[prop.name] = root[alias];
@@ -115,8 +206,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
115
206
  else {
116
207
  map[key] = result;
117
208
  }
118
- if ([core_1.ReferenceType.MANY_TO_MANY, core_1.ReferenceType.ONE_TO_MANY].includes(relation.reference)) {
119
- result[relation.name] = result[relation.name] || [];
209
+ if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
210
+ result[relation.name] ??= [];
120
211
  this.appendToCollection(meta2, result[relation.name], relationPojo);
121
212
  }
122
213
  else {
@@ -137,23 +228,28 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
137
228
  collection.push(relationPojo);
138
229
  }
139
230
  }
140
- async count(entityName, where, options = {}, ctx) {
231
+ async count(entityName, where, options = {}) {
141
232
  const meta = this.metadata.find(entityName);
142
- const pks = meta.primaryKeys;
143
- const qb = this.createQueryBuilder(entityName, ctx, !!ctx, false)
233
+ if (meta?.virtual) {
234
+ return this.countVirtual(entityName, where, options);
235
+ }
236
+ const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false)
237
+ .indexHint(options.indexHint)
238
+ .comment(options.comments)
239
+ .hintComment(options.hintComments)
144
240
  .groupBy(options.groupBy)
145
241
  .having(options.having)
146
242
  .populate(options.populate ?? [])
147
243
  .withSchema(this.getSchemaName(meta, options))
148
244
  .where(where);
149
- return this.rethrow(qb.getCount(pks, true));
245
+ return this.rethrow(qb.getCount());
150
246
  }
151
247
  async nativeInsert(entityName, data, options = {}) {
152
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
248
+ options.convertCustomTypes ??= true;
153
249
  const meta = this.metadata.find(entityName);
154
250
  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));
251
+ const pks = meta?.primaryKeys ?? [this.config.getNamingStrategy().referenceColumnName()];
252
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
157
253
  const res = await this.rethrow(qb.insert(data).execute('run', false));
158
254
  res.row = res.row || {};
159
255
  let pk;
@@ -161,68 +257,90 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
161
257
  pk = core_1.Utils.getPrimaryKeyCond(data, pks);
162
258
  }
163
259
  else {
164
- res.insertId = data[pks[0]] || res.insertId || res.row[pks[0]];
260
+ /* istanbul ignore next */
261
+ res.insertId = data[pks[0]] ?? res.insertId ?? res.row[pks[0]];
165
262
  pk = [res.insertId];
166
263
  }
167
264
  await this.processManyToMany(meta, pk, collections, false, options);
168
265
  return res;
169
266
  }
170
267
  async nativeInsertMany(entityName, data, options = {}) {
171
- options.processCollections ?? (options.processCollections = true);
172
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
173
- const meta = this.metadata.get(entityName);
268
+ options.processCollections ??= true;
269
+ options.convertCustomTypes ??= true;
270
+ const meta = this.metadata.find(entityName);
174
271
  const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
175
272
  const pks = this.getPrimaryKeyFields(entityName);
176
273
  const set = new Set();
177
- data.forEach(row => Object.keys(row).forEach(k => set.add(k)));
178
- const props = [...set].map(name => meta.properties[name] ?? { name, fieldNames: [name] });
179
- 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));
274
+ data.forEach(row => core_1.Utils.keys(row).forEach(k => set.add(k)));
275
+ const props = [...set].map(name => meta?.properties[name] ?? { name, fieldNames: [name] });
276
+ let fields = core_1.Utils.flatten(props.map(prop => prop.fieldNames));
277
+ const duplicates = core_1.Utils.findDuplicates(fields);
278
+ const params = [];
279
+ if (duplicates.length) {
280
+ fields = core_1.Utils.unique(fields);
281
+ }
282
+ /* istanbul ignore next */
283
+ const tableName = meta ? this.getTableName(meta, options) : this.platform.quoteIdentifier(entityName);
284
+ let sql = `insert into ${tableName} `;
285
+ sql += fields.length > 0 ? '(' + fields.map(k => this.platform.quoteIdentifier(k)).join(', ') + ')' : `(${this.platform.quoteIdentifier(pks[0])})`;
286
+ if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
287
+ sql += ' values ';
184
288
  }
185
289
  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 = [];
290
+ sql += ' ' + data.map(() => `select null as ${this.platform.quoteIdentifier(pks[0])}`).join(' union all ');
291
+ }
292
+ const addParams = (prop, row) => {
293
+ if (options.convertCustomTypes && prop.customType) {
294
+ params.push(prop.customType.convertToDatabaseValue(row[prop.name], this.platform, { key: prop.name, mode: 'query-data' }));
295
+ return;
296
+ }
297
+ params.push(row[prop.name]);
298
+ };
299
+ if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
191
300
  sql += data.map(row => {
192
301
  const keys = [];
193
302
  props.forEach(prop => {
194
303
  if (prop.fieldNames.length > 1) {
195
- params.push(...row[prop.name]);
196
- keys.push(...prop.fieldNames.map(_ => '?'));
304
+ const param = [...row[prop.name] ?? prop.fieldNames.map(() => null)];
305
+ const key = (row[prop.name] ?? prop.fieldNames).map(() => '?');
306
+ prop.fieldNames.forEach((field, idx) => {
307
+ if (duplicates.includes(field)) {
308
+ param.splice(idx, 1);
309
+ key.splice(idx, 1);
310
+ }
311
+ });
312
+ params.push(...param);
313
+ keys.push(...key);
197
314
  }
198
315
  else if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(row[prop.name])) {
199
316
  keys.push(prop.customType.convertToDatabaseValueSQL('?', this.platform));
200
- params.push(row[prop.name]);
317
+ addParams(prop, row);
201
318
  }
202
319
  else {
203
- params.push(row[prop.name]);
320
+ addParams(prop, row);
204
321
  keys.push('?');
205
322
  }
206
323
  });
207
- return '(' + keys.join(', ') + ')';
324
+ return '(' + (keys.join(', ') || 'default') + ')';
208
325
  }).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
326
  }
327
+ if (meta && this.platform.usesReturningStatement()) {
328
+ const returningProps = meta.props
329
+ .filter(prop => prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw))
330
+ .filter(prop => !(prop.name in data[0]) || core_1.Utils.isRawSql(data[0][prop.name]));
331
+ const returningFields = core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames));
332
+ /* istanbul ignore next */
333
+ sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
334
+ }
335
+ const res = await this.execute(sql, params, 'run', options.ctx);
218
336
  let pk;
219
337
  /* istanbul ignore next */
220
338
  if (pks.length > 1) { // owner has composite pk
221
339
  pk = data.map(d => core_1.Utils.getPrimaryKeyCond(d, pks));
222
340
  }
223
341
  else {
224
- res.row ?? (res.row = {});
225
- res.rows ?? (res.rows = []);
342
+ res.row ??= {};
343
+ res.rows ??= [];
226
344
  pk = data.map((d, i) => d[pks[0]] ?? res.rows[i]?.[pks[0]]).map(d => [d]);
227
345
  res.insertId = res.insertId || res.row[pks[0]];
228
346
  }
@@ -232,19 +350,36 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
232
350
  return res;
233
351
  }
234
352
  async nativeUpdate(entityName, where, data, options = {}) {
235
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
353
+ options.convertCustomTypes ??= true;
236
354
  const meta = this.metadata.find(entityName);
237
355
  const pks = this.getPrimaryKeyFields(entityName);
238
356
  const collections = this.extractManyToMany(entityName, data);
239
357
  let res = { affectedRows: 0, insertId: 0, row: {} };
240
358
  if (core_1.Utils.isPrimaryKey(where) && pks.length === 1) {
241
- where = { [pks[0]]: where };
359
+ /* istanbul ignore next */
360
+ where = { [meta?.primaryKeys[0] ?? pks[0]]: where };
242
361
  }
243
362
  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);
363
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes)
364
+ .withSchema(this.getSchemaName(meta, options));
365
+ if (options.upsert) {
366
+ /* istanbul ignore next */
367
+ const uniqueFields = options.onConflictFields ?? (core_1.Utils.isPlainObject(where) ? core_1.Utils.keys(where) : meta.primaryKeys);
368
+ const returning = (0, core_1.getOnConflictReturningFields)(meta, data, uniqueFields, options);
369
+ qb.insert(data)
370
+ .onConflict(uniqueFields)
371
+ .returning(returning);
372
+ if (!options.onConflictAction || options.onConflictAction === 'merge') {
373
+ const fields = (0, core_1.getOnConflictFields)(data, uniqueFields, options);
374
+ qb.merge(fields);
375
+ }
376
+ if (options.onConflictAction === 'ignore') {
377
+ qb.ignore();
378
+ }
379
+ }
380
+ else {
381
+ qb.update(data).where(where);
382
+ }
248
383
  res = await this.rethrow(qb.execute('run', false));
249
384
  }
250
385
  /* istanbul ignore next */
@@ -253,12 +388,36 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
253
388
  return res;
254
389
  }
255
390
  async nativeUpdateMany(entityName, where, data, options = {}) {
256
- options.processCollections ?? (options.processCollections = true);
257
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
391
+ options.processCollections ??= true;
392
+ options.convertCustomTypes ??= true;
258
393
  const meta = this.metadata.get(entityName);
394
+ if (options.upsert) {
395
+ const uniqueFields = options.onConflictFields ?? (core_1.Utils.isPlainObject(where[0]) ? Object.keys(where[0]) : meta.primaryKeys);
396
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
397
+ const returning = (0, core_1.getOnConflictReturningFields)(meta, data[0], uniqueFields, options);
398
+ qb.insert(data)
399
+ .onConflict(uniqueFields)
400
+ .returning(returning);
401
+ if (!options.onConflictAction || options.onConflictAction === 'merge') {
402
+ const fields = (0, core_1.getOnConflictFields)(data[0], uniqueFields, options);
403
+ qb.merge(fields);
404
+ }
405
+ if (options.onConflictAction === 'ignore') {
406
+ qb.ignore();
407
+ }
408
+ return qb.execute('run', false);
409
+ }
259
410
  const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
260
411
  const keys = new Set();
261
- data.forEach(row => Object.keys(row).forEach(k => keys.add(k)));
412
+ const returning = new Set();
413
+ data.forEach(row => {
414
+ core_1.Utils.keys(row).forEach(k => {
415
+ keys.add(k);
416
+ if (core_1.Utils.isRawSql(row[k])) {
417
+ returning.add(k);
418
+ }
419
+ });
420
+ });
262
421
  const pkCond = core_1.Utils.flatten(meta.primaryKeys.map(pk => meta.properties[pk].fieldNames)).map(pk => `${this.platform.quoteIdentifier(pk)} = ?`).join(' and ');
263
422
  const params = [];
264
423
  let sql = `update ${this.getTableName(meta, options)} set `;
@@ -276,7 +435,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
276
435
  else {
277
436
  sql += '?';
278
437
  }
279
- params.push(...pks, prop.fieldNames.length > 1 ? data[idx][key][fieldNameIdx] : data[idx][key]);
438
+ params.push(...pks, prop.fieldNames.length > 1 ? data[idx][key]?.[fieldNameIdx] : data[idx][key]);
280
439
  }
281
440
  });
282
441
  sql += ` else ${this.platform.quoteIdentifier(fieldName)} end, `;
@@ -287,7 +446,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
287
446
  const versionProperty = meta.properties[meta.versionProperty];
288
447
  const quotedFieldName = this.platform.quoteIdentifier(versionProperty.fieldNames[0]);
289
448
  sql += `${quotedFieldName} = `;
290
- if (versionProperty.type.toLowerCase() === 'date') {
449
+ if (versionProperty.runtimeType === 'Date') {
291
450
  sql += this.platform.getCurrentTimestampSQL(versionProperty.length);
292
451
  }
293
452
  else {
@@ -300,15 +459,29 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
300
459
  const pks = core_1.Utils.flatten(pkProps.map(pk => meta.properties[pk].fieldNames));
301
460
  sql += pks.length > 1 ? `(${pks.map(pk => `${this.platform.quoteIdentifier(pk)}`).join(', ')})` : this.platform.quoteIdentifier(pks[0]);
302
461
  const conds = where.map(cond => {
462
+ if (core_1.Utils.isPlainObject(cond) && core_1.Utils.getObjectKeysSize(cond) === 1) {
463
+ cond = Object.values(cond)[0];
464
+ }
303
465
  if (pks.length > 1) {
304
- pkProps.forEach(pk => params.push(cond[pk]));
466
+ pkProps.forEach(pk => {
467
+ if (Array.isArray(cond[pk])) {
468
+ params.push(...core_1.Utils.flatten(cond[pk]));
469
+ }
470
+ else {
471
+ params.push(cond[pk]);
472
+ }
473
+ });
305
474
  return `(${new Array(pks.length).fill('?').join(', ')})`;
306
475
  }
307
476
  params.push(cond);
308
477
  return '?';
309
478
  });
310
- const values = pks.length > 1 && this.platform.requiresValuesKeyword() ? 'values ' : '';
311
- sql += ` in (${values}${conds.join(', ')})`;
479
+ sql += ` in (${conds.join(', ')})`;
480
+ if (this.platform.usesReturningStatement() && returning.size > 0) {
481
+ const returningFields = core_1.Utils.flatten([...returning].map(prop => meta.properties[prop].fieldNames));
482
+ /* istanbul ignore next */
483
+ sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
484
+ }
312
485
  const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx));
313
486
  for (let i = 0; i < collections.length; i++) {
314
487
  await this.processManyToMany(meta, where[i], collections[i], false, options);
@@ -321,17 +494,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
321
494
  if (core_1.Utils.isPrimaryKey(where) && pks.length === 1) {
322
495
  where = { [pks[0]]: where };
323
496
  }
324
- const qb = this.createQueryBuilder(entityName, options.ctx, true, false).delete(where).withSchema(this.getSchemaName(meta, options));
497
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false).delete(where).withSchema(this.getSchemaName(meta, options));
325
498
  return this.rethrow(qb.execute('run', false));
326
499
  }
327
500
  async syncCollection(coll, options) {
328
- const wrapped = coll.owner.__helper;
501
+ const wrapped = (0, core_1.helper)(coll.owner);
329
502
  const meta = wrapped.__meta;
330
503
  const pks = wrapped.getPrimaryKeys(true);
331
504
  const snap = coll.getSnapshot();
332
505
  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));
506
+ const snapshot = snap ? snap.map(item => (0, core_1.helper)(item).getPrimaryKeys(true)) : [];
507
+ const current = coll.getItems(false).map(item => (0, core_1.helper)(item).getPrimaryKeys(true));
335
508
  const deleteDiff = snap ? snapshot.filter(item => !includes(current, item)) : true;
336
509
  const insertDiff = current.filter(item => !includes(snapshot, item));
337
510
  const target = snapshot.filter(item => includes(current, item)).concat(...insertDiff);
@@ -343,29 +516,53 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
343
516
  deleteDiff.push(...snapshot);
344
517
  insertDiff.push(...current);
345
518
  }
346
- if (coll.property.reference === core_1.ReferenceType.ONE_TO_MANY) {
519
+ if (coll.property.kind === core_1.ReferenceKind.ONE_TO_MANY) {
347
520
  const cols = coll.property.referencedColumnNames;
348
- const qb = this.createQueryBuilder(coll.property.type, ctx, true)
349
- .withSchema(this.getSchemaName(meta, options))
350
- .update({ [coll.property.mappedBy]: pks })
521
+ const qb = this.createQueryBuilder(coll.property.type, ctx, 'write')
522
+ .withSchema(this.getSchemaName(meta, options));
523
+ if (coll.getSnapshot() === undefined) {
524
+ if (coll.property.orphanRemoval) {
525
+ const kqb = qb.delete({ [coll.property.mappedBy]: pks })
526
+ .getKnexQuery()
527
+ .whereNotIn(cols, insertDiff);
528
+ return this.rethrow(this.execute(kqb));
529
+ }
530
+ const kqb = qb.update({ [coll.property.mappedBy]: null })
531
+ .getKnexQuery()
532
+ .whereNotIn(cols, insertDiff);
533
+ return this.rethrow(this.execute(kqb));
534
+ }
535
+ const kqb = qb.update({ [coll.property.mappedBy]: pks })
351
536
  .getKnexQuery()
352
537
  .whereIn(cols, insertDiff);
353
- return this.rethrow(this.execute(qb));
538
+ return this.rethrow(this.execute(kqb));
354
539
  }
355
540
  /* istanbul ignore next */
356
541
  const ownerSchema = wrapped.getSchema() === '*' ? this.config.get('schema') : wrapped.getSchema();
357
- const pivotMeta = this.metadata.find(coll.property.pivotTable);
542
+ const pivotMeta = this.metadata.find(coll.property.pivotEntity);
358
543
  if (pivotMeta.schema === '*') {
359
544
  /* istanbul ignore next */
360
- options ?? (options = {});
545
+ options ??= {};
361
546
  options.schema = ownerSchema;
362
547
  }
363
548
  return this.rethrow(this.updateCollectionDiff(meta, coll.property, pks, deleteDiff, insertDiff, options));
364
549
  }
365
550
  async loadFromPivotTable(prop, owners, where = {}, orderBy, ctx, options) {
366
551
  const pivotProp2 = this.getPivotInverseProperty(prop);
552
+ const prop2 = prop.targetMeta.properties[prop.mappedBy ?? prop.inversedBy];
367
553
  const ownerMeta = this.metadata.find(pivotProp2.type);
368
- const cond = { [`${prop.pivotTable}.${pivotProp2.name}`]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) } };
554
+ const pivotMeta = this.metadata.find(prop.pivotEntity);
555
+ options = { ...options };
556
+ const qb = this.createQueryBuilder(prop.type, ctx, options.connectionType)
557
+ .withSchema(this.getSchemaName(prop.targetMeta, options))
558
+ .indexHint(options.indexHint)
559
+ .comment(options.comments)
560
+ .hintComment(options.hintComments);
561
+ const pivotAlias = qb.getNextAlias(pivotMeta.tableName);
562
+ const pivotKey = pivotProp2.joinColumns.map(column => `${pivotAlias}.${column}`).join(core_1.Utils.PK_SEPARATOR);
563
+ const cond = {
564
+ [pivotKey]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) },
565
+ };
369
566
  /* istanbul ignore if */
370
567
  if (!core_1.Utils.isEmpty(where) && Object.keys(where).every(k => core_1.Utils.isOperator(k, false))) {
371
568
  where = cond;
@@ -373,16 +570,34 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
373
570
  else {
374
571
  where = { ...where, ...cond };
375
572
  }
376
- orderBy = this.getPivotOrderBy(prop, orderBy);
377
- const qb = this.createQueryBuilder(prop.type, ctx, !!ctx)
378
- .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES)
379
- .withSchema(this.getSchemaName(prop.targetMeta, options));
380
- const populate = this.autoJoinOneToOneOwner(prop.targetMeta, [{ field: prop.pivotTable }]);
381
- const fields = this.buildFields(prop.targetMeta, (options?.populate ?? []), [], qb, options?.fields);
382
- qb.select(fields).populate(populate).where(where).orderBy(orderBy).setLockMode(options?.lockMode, options?.lockTableAliases);
383
- if (owners.length === 1 && (options?.offset != null || options?.limit != null)) {
573
+ orderBy = this.getPivotOrderBy(prop, pivotAlias, orderBy);
574
+ const populate = this.autoJoinOneToOneOwner(prop.targetMeta, []);
575
+ const fields = this.buildFields(prop.targetMeta, (options.populate ?? []), [], qb, options.fields);
576
+ const k1 = !prop.owner ? 'joinColumns' : 'inverseJoinColumns';
577
+ const k2 = prop.owner ? 'joinColumns' : 'inverseJoinColumns';
578
+ const cols = [
579
+ ...prop[k1].map(col => `${pivotAlias}.${col} as fk__${col}`),
580
+ ...prop[k2].map(col => `${pivotAlias}.${col} as fk__${col}`),
581
+ ];
582
+ fields.push(...cols);
583
+ qb.select(fields)
584
+ .join(prop2.name, pivotAlias, {}, 'pivotJoin', undefined, options.schema)
585
+ .populate(populate)
586
+ .where(where)
587
+ .orderBy(orderBy)
588
+ .setLockMode(options.lockMode, options.lockTableAliases);
589
+ if (owners.length === 1 && (options.offset != null || options.limit != null)) {
384
590
  qb.limit(options.limit, options.offset);
385
591
  }
592
+ const schema = this.config.get('schema');
593
+ if (prop.targetMeta.schema !== '*' && pivotMeta.schema === '*' && options.schema) {
594
+ // eslint-disable-next-line dot-notation
595
+ qb['finalize']();
596
+ // eslint-disable-next-line dot-notation
597
+ Object.values(qb['_joins']).forEach(join => {
598
+ join.schema = schema;
599
+ });
600
+ }
386
601
  const items = owners.length ? await this.rethrow(qb.execute('all')) : [];
387
602
  const map = {};
388
603
  const pkProps = ownerMeta.getPrimaryProps();
@@ -404,52 +619,62 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
404
619
  });
405
620
  return map;
406
621
  }
407
- async execute(queryOrKnex, params = [], method = 'all', ctx) {
408
- return this.rethrow(this.connection.execute(queryOrKnex, params, method, ctx));
409
- }
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;
622
+ getPivotOrderBy(prop, pivotAlias, orderBy) {
623
+ if (!core_1.Utils.isEmpty(orderBy)) {
624
+ return orderBy;
414
625
  }
415
- if (meta.schema || options.schema) {
416
- tableName = this.platform.quoteIdentifier(options.schema ?? meta.schema) + '.' + tableName;
626
+ if (!core_1.Utils.isEmpty(prop.orderBy)) {
627
+ return core_1.Utils.asArray(prop.orderBy);
417
628
  }
418
- return tableName;
629
+ if (prop.fixedOrder) {
630
+ return [{ [`${pivotAlias}.${prop.fixedOrderColumn}`]: core_1.QueryOrder.ASC }];
631
+ }
632
+ return [];
633
+ }
634
+ async execute(queryOrKnex, params = [], method = 'all', ctx) {
635
+ return this.rethrow(this.connection.execute(queryOrKnex, params, method, ctx));
419
636
  }
420
637
  /**
421
638
  * 1:1 owner side needs to be marked for population so QB auto-joins the owner id
422
639
  */
423
640
  autoJoinOneToOneOwner(meta, populate, fields = []) {
424
- if (!this.config.get('autoJoinOneToOneOwner') || fields.length > 0) {
641
+ if (!this.config.get('autoJoinOneToOneOwner')) {
425
642
  return populate;
426
643
  }
427
644
  const relationsToPopulate = populate.map(({ field }) => field);
428
645
  const toPopulate = meta.relations
429
- .filter(prop => prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
646
+ .filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
647
+ .filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
430
648
  .map(prop => ({ field: prop.name, strategy: prop.strategy }));
431
649
  return [...populate, ...toPopulate];
432
650
  }
433
651
  joinedProps(meta, populate) {
434
652
  return populate.filter(p => {
435
653
  const prop = meta.properties[p.field] || {};
436
- return (p.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && prop.reference !== core_1.ReferenceType.SCALAR;
654
+ return (p.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && prop.kind !== core_1.ReferenceKind.SCALAR;
437
655
  });
438
656
  }
439
657
  /**
440
658
  * @internal
441
659
  */
442
- mergeJoinedResult(rawResults, meta) {
660
+ mergeJoinedResult(rawResults, meta, joinedProps) {
443
661
  // group by the root entity primary key first
444
662
  const map = {};
445
663
  const res = [];
446
664
  rawResults.forEach(item => {
447
665
  const pk = core_1.Utils.getCompositeKeyHash(item, meta);
448
666
  if (map[pk]) {
449
- map[pk].push(item);
667
+ joinedProps.forEach(hint => {
668
+ // Sometimes we might join a M:N relation with additional filter on the target entity, and as a result, we get
669
+ // the first result with `null` for all target values, which is mapped as empty array. When we see that happen,
670
+ // we need to merge the results of the next item.
671
+ if (Array.isArray(map[pk][hint.field]) && Array.isArray(item[hint.field]) && map[pk][hint.field].length === 0) {
672
+ item[hint.field].forEach((el) => map[pk][hint.field].push(el));
673
+ }
674
+ });
450
675
  }
451
676
  else {
452
- map[pk] = [item];
677
+ map[pk] = item;
453
678
  res.push(item);
454
679
  }
455
680
  });
@@ -458,14 +683,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
458
683
  getFieldsForJoinedLoad(qb, meta, explicitFields, populate = [], parentTableAlias, parentJoinPath) {
459
684
  const fields = [];
460
685
  const joinedProps = this.joinedProps(meta, populate);
686
+ if (explicitFields?.includes('*')) {
687
+ fields.push('*');
688
+ }
461
689
  const shouldHaveColumn = (prop, populate, fields) => {
462
690
  if (!this.platform.shouldHaveColumn(prop, populate)) {
463
691
  return false;
464
692
  }
465
693
  if (!fields || prop.primary) {
466
- return true;
694
+ return !fields?.includes('*');
467
695
  }
468
- return fields.includes(prop.name);
696
+ return fields.some(f => f === prop.name || f.toString().startsWith(prop.name + '.'));
469
697
  };
470
698
  // alias all fields in the primary table
471
699
  meta.props
@@ -492,34 +720,49 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
492
720
  * @internal
493
721
  */
494
722
  mapPropToFieldNames(qb, prop, tableAlias) {
495
- const aliased = qb.ref(tableAlias ? `${tableAlias}__${prop.fieldNames[0]}` : prop.fieldNames[0]).toString();
723
+ const knex = this.connection.getKnex();
724
+ const aliased = knex.ref(tableAlias ? `${tableAlias}__${prop.fieldNames[0]}` : prop.fieldNames[0]).toString();
496
725
  if (prop.customType?.convertToJSValueSQL && tableAlias) {
497
- const prefixed = qb.ref(prop.fieldNames[0]).withSchema(tableAlias).toString();
498
- return [qb.raw(prop.customType.convertToJSValueSQL(prefixed, this.platform) + ' as ' + aliased).toString()];
726
+ const prefixed = knex.ref(prop.fieldNames[0]).withSchema(tableAlias).toString();
727
+ return [(0, core_1.raw)(`${prop.customType.convertToJSValueSQL(prefixed, this.platform)} as ${aliased}`)];
499
728
  }
500
729
  if (prop.formula) {
501
- const alias = qb.ref(tableAlias ?? qb.alias).toString();
502
- return [`${prop.formula(alias)} as ${aliased}`];
730
+ const alias = knex.ref(tableAlias ?? qb.alias).toString();
731
+ return [(0, core_1.raw)(`${prop.formula(alias)} as ${aliased}`)];
503
732
  }
504
733
  if (tableAlias) {
505
- return prop.fieldNames.map(fieldName => qb.ref(fieldName).withSchema(tableAlias).as(`${tableAlias}__${fieldName}`));
734
+ return prop.fieldNames.map(fieldName => knex.ref(fieldName).withSchema(tableAlias).as(`${tableAlias}__${fieldName}`));
506
735
  }
507
736
  return prop.fieldNames;
508
737
  }
509
- createQueryBuilder(entityName, ctx, write, convertCustomTypes) {
510
- const qb = new QueryBuilder_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, write ? 'write' : 'read');
738
+ /** @internal */
739
+ createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes, logging) {
740
+ const connectionType = this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
741
+ const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType, undefined, logging);
511
742
  if (!convertCustomTypes) {
512
743
  qb.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
513
744
  }
514
745
  return qb;
515
746
  }
747
+ resolveConnectionType(args) {
748
+ if (args.ctx) {
749
+ return 'write';
750
+ }
751
+ else if (args.connectionType) {
752
+ return args.connectionType;
753
+ }
754
+ else if (this.config.get('preferReadReplicas') === true) {
755
+ return 'read';
756
+ }
757
+ return 'write';
758
+ }
516
759
  extractManyToMany(entityName, data) {
517
760
  if (!this.metadata.has(entityName)) {
518
761
  return {};
519
762
  }
520
763
  const ret = {};
521
764
  this.metadata.find(entityName).relations.forEach(prop => {
522
- if (prop.reference === core_1.ReferenceType.MANY_TO_MANY && data[prop.name]) {
765
+ if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && data[prop.name]) {
523
766
  ret[prop.name] = data[prop.name].map((item) => core_1.Utils.asArray(item));
524
767
  delete data[prop.name];
525
768
  }
@@ -540,10 +783,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
540
783
  if (!deleteDiff) {
541
784
  deleteDiff = [];
542
785
  }
786
+ const pivotMeta = this.metadata.find(prop.pivotEntity);
543
787
  if (deleteDiff === true || deleteDiff.length > 0) {
544
- const qb1 = this.createQueryBuilder(prop.pivotTable, options?.ctx, true)
545
- .withSchema(this.getSchemaName(meta, options))
546
- .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
788
+ const qb1 = this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write').withSchema(this.getSchemaName(pivotMeta, options));
547
789
  const knex = qb1.getKnex();
548
790
  if (Array.isArray(deleteDiff)) {
549
791
  knex.whereIn(prop.inverseJoinColumns, deleteDiff);
@@ -562,23 +804,26 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
562
804
  });
563
805
  /* istanbul ignore else */
564
806
  if (this.platform.allowsMultiInsert()) {
565
- await this.nativeInsertMany(prop.pivotTable, items, { ...options, convertCustomTypes: false, processCollections: false });
807
+ await this.nativeInsertMany(prop.pivotEntity, items, {
808
+ ...options,
809
+ convertCustomTypes: false,
810
+ processCollections: false,
811
+ });
566
812
  }
567
813
  else {
568
814
  await core_1.Utils.runSerial(items, item => {
569
- return this.createQueryBuilder(prop.pivotTable, options?.ctx, true)
570
- .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES)
571
- .withSchema(this.getSchemaName(meta, options))
815
+ return this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
816
+ .withSchema(this.getSchemaName(pivotMeta, options))
572
817
  .insert(item)
573
818
  .execute('run', false);
574
819
  });
575
820
  }
576
821
  }
577
822
  async lockPessimistic(entity, options) {
578
- const meta = entity.__helper.__meta;
579
- const qb = this.createQueryBuilder(entity.constructor.name, options.ctx).unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES).withSchema(options.schema ?? meta.schema);
823
+ const meta = (0, core_1.helper)(entity).__meta;
824
+ const qb = this.createQueryBuilder(entity.constructor.name, options.ctx).withSchema(options.schema ?? meta.schema);
580
825
  const cond = core_1.Utils.getPrimaryKeyCond(entity, meta.primaryKeys);
581
- qb.select('1').where(cond).setLockMode(options.lockMode, options.lockTableAliases);
826
+ qb.select((0, core_1.raw)('1')).where(cond).setLockMode(options.lockMode, options.lockTableAliases);
582
827
  await this.rethrow(qb.execute());
583
828
  }
584
829
  buildJoinedPropsOrderBy(entityName, qb, meta, populate, parentPath) {
@@ -590,7 +835,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
590
835
  const path = `${parentPath ? parentPath : entityName}.${relation.field}`;
591
836
  const propAlias = qb.getAliasForJoinPath(path);
592
837
  if (propOrderBy) {
593
- Object.keys(propOrderBy).forEach(field => {
838
+ core_1.Utils.keys(propOrderBy).forEach(field => {
594
839
  orderBy.push({ [`${propAlias}.${field}`]: propOrderBy[field] });
595
840
  });
596
841
  }
@@ -601,44 +846,92 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
601
846
  });
602
847
  return orderBy;
603
848
  }
849
+ normalizeFields(fields, prefix = '') {
850
+ const ret = [];
851
+ for (const field of fields) {
852
+ if (typeof field === 'string') {
853
+ ret.push(prefix + field);
854
+ continue;
855
+ }
856
+ if (core_1.Utils.isPlainObject(field)) {
857
+ for (const key of Object.keys(field)) {
858
+ ret.push(...this.normalizeFields(field[key], key + '.'));
859
+ }
860
+ }
861
+ }
862
+ return ret;
863
+ }
864
+ processField(meta, prop, field, ret, populate, joinedProps, qb) {
865
+ if (!prop || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner)) {
866
+ return;
867
+ }
868
+ if (prop.kind === core_1.ReferenceKind.EMBEDDED) {
869
+ if (prop.object) {
870
+ ret.push(prop.name);
871
+ return;
872
+ }
873
+ const parts = field.split('.');
874
+ const top = parts.shift();
875
+ for (const key of Object.keys(prop.embeddedProps)) {
876
+ if (!top || key === top) {
877
+ this.processField(meta, prop.embeddedProps[key], parts.join('.'), ret, populate, joinedProps, qb);
878
+ }
879
+ }
880
+ return;
881
+ }
882
+ ret.push(prop.name);
883
+ }
604
884
  buildFields(meta, populate, joinedProps, qb, fields) {
605
885
  const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => p.field === prop.name || p.all));
606
886
  const hasLazyFormulas = meta.props.some(p => p.lazy && p.formula);
607
887
  const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL);
608
888
  const hasExplicitFields = !!fields;
609
889
  const ret = [];
890
+ let addFormulas = false;
610
891
  if (joinedProps.length > 0) {
611
892
  ret.push(...this.getFieldsForJoinedLoad(qb, meta, fields, populate));
612
893
  }
613
894
  else if (fields) {
614
- for (const field of [...fields]) {
615
- if (core_1.Utils.isPlainObject(field) || field.toString().includes('.')) {
895
+ for (const field of this.normalizeFields(fields)) {
896
+ if (field === '*') {
897
+ ret.push('*');
616
898
  continue;
617
899
  }
618
- ret.push(field);
900
+ const parts = field.split('.');
901
+ const rootPropName = parts.shift(); // first one is the `prop`
902
+ const prop = core_1.QueryHelper.findProperty(rootPropName, {
903
+ metadata: this.metadata,
904
+ platform: this.platform,
905
+ entityName: meta.className,
906
+ where: {},
907
+ aliasMap: qb.getAliasMap(),
908
+ });
909
+ this.processField(meta, prop, parts.join('.'), ret, populate, joinedProps, qb);
619
910
  }
620
911
  ret.unshift(...meta.primaryKeys.filter(pk => !fields.includes(pk)));
621
912
  }
622
913
  else if (lazyProps.filter(p => !p.formula).length > 0) {
623
914
  const props = meta.props.filter(prop => this.platform.shouldHaveColumn(prop, populate, false));
624
915
  ret.push(...core_1.Utils.flatten(props.filter(p => !lazyProps.includes(p)).map(p => p.fieldNames)));
916
+ addFormulas = true;
625
917
  }
626
918
  else if (hasLazyFormulas || requiresSQLConversion) {
627
919
  ret.push('*');
920
+ addFormulas = true;
628
921
  }
629
- if (ret.length > 0 && !hasExplicitFields) {
922
+ if (ret.length > 0 && !hasExplicitFields && addFormulas) {
630
923
  meta.props
631
924
  .filter(prop => prop.formula && !lazyProps.includes(prop))
632
925
  .forEach(prop => {
633
- const alias = qb.ref(qb.alias).toString();
634
- const aliased = qb.ref(prop.fieldNames[0]).toString();
635
- ret.push(`${prop.formula(alias)} as ${aliased}`);
926
+ const alias = this.connection.getKnex().ref(qb.alias).toString();
927
+ const aliased = this.connection.getKnex().ref(prop.fieldNames[0]).toString();
928
+ ret.push((0, core_1.raw)(`${prop.formula(alias)} as ${aliased}`));
636
929
  });
637
930
  meta.props
638
- .filter(prop => prop.customType?.convertToDatabaseValueSQL || prop.customType?.convertToJSValueSQL)
931
+ .filter(prop => prop.hasConvertToDatabaseValueSQL || prop.hasConvertToJSValueSQL)
639
932
  .forEach(prop => ret.push(prop.name));
640
933
  }
641
- return ret.length > 0 ? ret : ['*'];
934
+ return ret.length > 0 ? core_1.Utils.unique(ret) : ['*'];
642
935
  }
643
936
  }
644
937
  exports.AbstractSqlDriver = AbstractSqlDriver;