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

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 (56) hide show
  1. package/{dist/AbstractSqlConnection.d.ts → AbstractSqlConnection.d.ts} +0 -0
  2. package/{dist/AbstractSqlConnection.js → AbstractSqlConnection.js} +6 -1
  3. package/AbstractSqlDriver.d.ts +66 -0
  4. package/{dist/AbstractSqlDriver.js → AbstractSqlDriver.js} +268 -93
  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 -4
  13. package/{dist/SqlEntityRepository.js → SqlEntityRepository.js} +1 -2
  14. package/{dist/index.d.ts → index.d.ts} +1 -0
  15. package/{dist/index.js → index.js} +6 -1
  16. package/index.mjs +194 -0
  17. package/package.json +18 -47
  18. package/query/ArrayCriteriaNode.d.ts +9 -0
  19. package/{dist/query → query}/ArrayCriteriaNode.js +0 -0
  20. package/{dist/query → query}/CriteriaNode.d.ts +8 -8
  21. package/{dist/query → query}/CriteriaNode.js +15 -12
  22. package/query/CriteriaNodeFactory.d.ts +12 -0
  23. package/{dist/query → query}/CriteriaNodeFactory.js +10 -10
  24. package/{dist/query → query}/ObjectCriteriaNode.d.ts +3 -3
  25. package/{dist/query → query}/ObjectCriteriaNode.js +19 -6
  26. package/{dist/query → query}/QueryBuilder.d.ts +66 -24
  27. package/{dist/query → query}/QueryBuilder.js +381 -94
  28. package/{dist/query → query}/QueryBuilderHelper.d.ts +19 -11
  29. package/{dist/query → query}/QueryBuilderHelper.js +227 -116
  30. package/{dist/query → query}/ScalarCriteriaNode.d.ts +2 -2
  31. package/{dist/query → query}/ScalarCriteriaNode.js +6 -6
  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 +11 -19
  38. package/{dist/schema → schema}/DatabaseTable.d.ts +9 -9
  39. package/{dist/schema → schema}/DatabaseTable.js +22 -22
  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} +10 -12
  45. package/{dist/schema/SchemaGenerator.js → schema/SqlSchemaGenerator.js} +89 -45
  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} +21 -16
  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
  55. package/dist/query/ArrayCriteriaNode.d.ts +0 -9
  56. package/dist/query/CriteriaNodeFactory.d.ts +0 -12
@@ -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,21 +21,33 @@ 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);
32
+ const orderBy = [...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy];
29
33
  if (core_1.Utils.isPrimaryKey(where, meta.compositePK)) {
30
34
  where = { [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
31
35
  }
36
+ const { first, last, before, after } = options;
37
+ const isCursorPagination = [first, last, before, after].some(v => v != null);
32
38
  qb.select(fields)
33
- .populate(populate)
39
+ .populate(populate, joinedProps.length > 0 ? options.populateWhere : undefined)
34
40
  .where(where)
35
- .orderBy([...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy])
36
41
  .groupBy(options.groupBy)
37
42
  .having(options.having)
38
43
  .withSchema(this.getSchemaName(meta, options));
44
+ if (isCursorPagination) {
45
+ const { orderBy: newOrderBy, where } = this.processCursorOptions(meta, options, orderBy);
46
+ qb.andWhere(where).orderBy(newOrderBy);
47
+ }
48
+ else {
49
+ qb.orderBy(orderBy);
50
+ }
39
51
  if (options.limit !== undefined) {
40
52
  qb.limit(options.limit, options.offset);
41
53
  }
@@ -44,6 +56,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
44
56
  }
45
57
  core_1.Utils.asArray(options.flags).forEach(flag => qb.setFlag(flag));
46
58
  const result = await this.rethrow(qb.execute('all'));
59
+ if (isCursorPagination && !first && !!last) {
60
+ result.reverse();
61
+ }
47
62
  if (joinedProps.length > 0) {
48
63
  return this.mergeJoinedResult(result, meta);
49
64
  }
@@ -57,9 +72,70 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
57
72
  if (joinedProps.length === 0) {
58
73
  opts.limit = 1;
59
74
  }
75
+ if (opts.limit > 0 && !opts.flags?.includes(core_1.QueryFlag.DISABLE_PAGINATE)) {
76
+ opts.flags ??= [];
77
+ opts.flags.push(core_1.QueryFlag.DISABLE_PAGINATE);
78
+ }
60
79
  const res = await this.find(entityName, where, opts);
61
80
  return res[0] || null;
62
81
  }
82
+ async findVirtual(entityName, where, options) {
83
+ const meta = this.metadata.get(entityName);
84
+ /* istanbul ignore next */
85
+ if (!meta.expression) {
86
+ return [];
87
+ }
88
+ if (typeof meta.expression === 'string') {
89
+ return this.wrapVirtualExpressionInSubquery(meta, meta.expression, where, options);
90
+ }
91
+ const em = this.createEntityManager(false);
92
+ em.setTransactionContext(options.ctx);
93
+ const res = meta.expression(em, where, options);
94
+ if (res instanceof (query_1.QueryBuilder)) {
95
+ return this.wrapVirtualExpressionInSubquery(meta, res.getFormattedQuery(), where, options);
96
+ }
97
+ /* istanbul ignore next */
98
+ return res;
99
+ }
100
+ async countVirtual(entityName, where, options) {
101
+ const meta = this.metadata.get(entityName);
102
+ /* istanbul ignore next */
103
+ if (!meta.expression) {
104
+ return 0;
105
+ }
106
+ if (typeof meta.expression === 'string') {
107
+ return this.wrapVirtualExpressionInSubquery(meta, meta.expression, where, options, query_1.QueryType.COUNT);
108
+ }
109
+ const em = this.createEntityManager(false);
110
+ em.setTransactionContext(options.ctx);
111
+ const res = meta.expression(em, where, options);
112
+ if (res instanceof (query_1.QueryBuilder)) {
113
+ return this.wrapVirtualExpressionInSubquery(meta, res.getFormattedQuery(), where, options, query_1.QueryType.COUNT);
114
+ }
115
+ /* istanbul ignore next */
116
+ return res;
117
+ }
118
+ async wrapVirtualExpressionInSubquery(meta, expression, where, options, type = query_1.QueryType.SELECT) {
119
+ const qb = this.createQueryBuilder(meta.className, options?.ctx, options.connectionType, options.convertCustomTypes)
120
+ .limit(options?.limit, options?.offset);
121
+ if (options.orderBy) {
122
+ qb.orderBy(options.orderBy);
123
+ }
124
+ qb.where(where);
125
+ const kqb = qb.getKnexQuery().clear('select');
126
+ if (type === query_1.QueryType.COUNT) {
127
+ kqb.select(qb.raw('count(*) as count'));
128
+ }
129
+ else { // select
130
+ kqb.select('*');
131
+ }
132
+ kqb.fromRaw(`(${expression}) as ${this.platform.quoteIdentifier(qb.alias)}`);
133
+ const res = await this.execute(kqb);
134
+ if (type === query_1.QueryType.COUNT) {
135
+ return res[0].count;
136
+ }
137
+ return res.map(row => this.mapResult(row, meta));
138
+ }
63
139
  mapResult(result, meta, populate = [], qb, map = {}) {
64
140
  const ret = super.mapResult(result, meta);
65
141
  /* istanbul ignore if */
@@ -90,8 +166,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
90
166
  }));
91
167
  if (!hasPK) {
92
168
  // 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] || [];
169
+ if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
170
+ result[relation.name] ??= [];
95
171
  }
96
172
  return;
97
173
  }
@@ -115,8 +191,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
115
191
  else {
116
192
  map[key] = result;
117
193
  }
118
- if ([core_1.ReferenceType.MANY_TO_MANY, core_1.ReferenceType.ONE_TO_MANY].includes(relation.reference)) {
119
- result[relation.name] = result[relation.name] || [];
194
+ if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
195
+ result[relation.name] ??= [];
120
196
  this.appendToCollection(meta2, result[relation.name], relationPojo);
121
197
  }
122
198
  else {
@@ -137,23 +213,25 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
137
213
  collection.push(relationPojo);
138
214
  }
139
215
  }
140
- async count(entityName, where, options = {}, ctx) {
216
+ async count(entityName, where, options = {}) {
141
217
  const meta = this.metadata.find(entityName);
142
- const pks = meta.primaryKeys;
143
- const qb = this.createQueryBuilder(entityName, ctx, !!ctx, false)
218
+ if (meta?.virtual) {
219
+ return this.countVirtual(entityName, where, options);
220
+ }
221
+ const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false)
144
222
  .groupBy(options.groupBy)
145
223
  .having(options.having)
146
224
  .populate(options.populate ?? [])
147
225
  .withSchema(this.getSchemaName(meta, options))
148
226
  .where(where);
149
- return this.rethrow(qb.getCount(pks, true));
227
+ return this.rethrow(qb.getCount());
150
228
  }
151
229
  async nativeInsert(entityName, data, options = {}) {
152
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
230
+ options.convertCustomTypes ??= true;
153
231
  const meta = this.metadata.find(entityName);
154
232
  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));
233
+ const pks = meta?.primaryKeys ?? [this.config.getNamingStrategy().referenceColumnName()];
234
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
157
235
  const res = await this.rethrow(qb.insert(data).execute('run', false));
158
236
  res.row = res.row || {};
159
237
  let pk;
@@ -161,39 +239,41 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
161
239
  pk = core_1.Utils.getPrimaryKeyCond(data, pks);
162
240
  }
163
241
  else {
164
- res.insertId = data[pks[0]] || res.insertId || res.row[pks[0]];
242
+ /* istanbul ignore next */
243
+ res.insertId = data[pks[0]] ?? res.insertId ?? res.row[pks[0]];
165
244
  pk = [res.insertId];
166
245
  }
167
246
  await this.processManyToMany(meta, pk, collections, false, options);
168
247
  return res;
169
248
  }
170
249
  async nativeInsertMany(entityName, data, options = {}) {
171
- options.processCollections ?? (options.processCollections = true);
172
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
173
- const meta = this.metadata.get(entityName);
250
+ options.processCollections ??= true;
251
+ options.convertCustomTypes ??= true;
252
+ const meta = this.metadata.find(entityName);
174
253
  const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
175
254
  const pks = this.getPrimaryKeyFields(entityName);
176
255
  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] });
256
+ data.forEach(row => core_1.Utils.keys(row).forEach(k => set.add(k)));
257
+ const props = [...set].map(name => meta?.properties[name] ?? { name, fieldNames: [name] });
179
258
  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));
259
+ const params = [];
260
+ /* istanbul ignore next */
261
+ const tableName = meta ? this.getTableName(meta, options) : this.platform.quoteIdentifier(entityName);
262
+ let sql = `insert into ${tableName} `;
263
+ sql += fields.length > 0 ? '(' + fields.map(k => this.platform.quoteIdentifier(k)).join(', ') + ')' : `(${this.platform.quoteIdentifier(pks[0])})`;
264
+ if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
265
+ sql += ' values ';
184
266
  }
185
267
  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 = [];
268
+ sql += ' ' + data.map(() => `select null as ${this.platform.quoteIdentifier(pks[0])}`).join(' union all ');
269
+ }
270
+ if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
191
271
  sql += data.map(row => {
192
272
  const keys = [];
193
273
  props.forEach(prop => {
194
274
  if (prop.fieldNames.length > 1) {
195
- params.push(...row[prop.name]);
196
- keys.push(...prop.fieldNames.map(_ => '?'));
275
+ params.push(...(row[prop.name] ?? prop.fieldNames.map(() => null)));
276
+ keys.push(...(row[prop.name] ?? prop.fieldNames).map(() => '?'));
197
277
  }
198
278
  else if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(row[prop.name])) {
199
279
  keys.push(prop.customType.convertToDatabaseValueSQL('?', this.platform));
@@ -204,25 +284,25 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
204
284
  keys.push('?');
205
285
  }
206
286
  });
207
- return '(' + keys.join(', ') + ')';
287
+ return '(' + (keys.join(', ') || 'default') + ')';
208
288
  }).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
289
  }
290
+ if (this.platform.usesReturningStatement()) {
291
+ /* istanbul ignore next */
292
+ const returningProps = meta.hydrateProps.filter(prop => prop.persist !== false && (prop.primary || prop.defaultRaw));
293
+ const returningFields = core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames));
294
+ /* istanbul ignore next */
295
+ sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
296
+ }
297
+ const res = await this.execute(sql, params, 'run', options.ctx);
218
298
  let pk;
219
299
  /* istanbul ignore next */
220
300
  if (pks.length > 1) { // owner has composite pk
221
301
  pk = data.map(d => core_1.Utils.getPrimaryKeyCond(d, pks));
222
302
  }
223
303
  else {
224
- res.row ?? (res.row = {});
225
- res.rows ?? (res.rows = []);
304
+ res.row ??= {};
305
+ res.rows ??= [];
226
306
  pk = data.map((d, i) => d[pks[0]] ?? res.rows[i]?.[pks[0]]).map(d => [d]);
227
307
  res.insertId = res.insertId || res.row[pks[0]];
228
308
  }
@@ -232,19 +312,29 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
232
312
  return res;
233
313
  }
234
314
  async nativeUpdate(entityName, where, data, options = {}) {
235
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
315
+ options.convertCustomTypes ??= true;
236
316
  const meta = this.metadata.find(entityName);
237
317
  const pks = this.getPrimaryKeyFields(entityName);
238
318
  const collections = this.extractManyToMany(entityName, data);
239
319
  let res = { affectedRows: 0, insertId: 0, row: {} };
240
320
  if (core_1.Utils.isPrimaryKey(where) && pks.length === 1) {
241
- where = { [pks[0]]: where };
321
+ /* istanbul ignore next */
322
+ where = { [meta?.primaryKeys[0] ?? pks[0]]: where };
242
323
  }
243
324
  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);
325
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes)
326
+ .withSchema(this.getSchemaName(meta, options));
327
+ if (options.upsert) {
328
+ /* istanbul ignore next */
329
+ const uniqueFields = core_1.Utils.isPlainObject(where) ? core_1.Utils.keys(where) : meta.primaryKeys;
330
+ /* istanbul ignore next */
331
+ qb.insert(data)
332
+ .onConflict(uniqueFields.map(p => meta?.properties[p]?.fieldNames[0] ?? p))
333
+ .merge(core_1.Utils.keys(data).filter(f => !uniqueFields.includes(f)));
334
+ }
335
+ else {
336
+ qb.update(data).where(where);
337
+ }
248
338
  res = await this.rethrow(qb.execute('run', false));
249
339
  }
250
340
  /* istanbul ignore next */
@@ -253,12 +343,20 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
253
343
  return res;
254
344
  }
255
345
  async nativeUpdateMany(entityName, where, data, options = {}) {
256
- options.processCollections ?? (options.processCollections = true);
257
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
346
+ options.processCollections ??= true;
347
+ options.convertCustomTypes ??= true;
258
348
  const meta = this.metadata.get(entityName);
349
+ if (options.upsert) {
350
+ const uniqueFields = core_1.Utils.isPlainObject(where[0]) ? Object.keys(where[0]) : meta.primaryKeys;
351
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
352
+ qb.insert(data)
353
+ .onConflict(uniqueFields.map(p => meta?.properties[p]?.fieldNames[0] ?? p))
354
+ .merge(Object.keys(data[0]).filter(f => !uniqueFields.includes(f)));
355
+ return qb.execute('run', false);
356
+ }
259
357
  const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
260
358
  const keys = new Set();
261
- data.forEach(row => Object.keys(row).forEach(k => keys.add(k)));
359
+ data.forEach(row => core_1.Utils.keys(row).forEach(k => keys.add(k)));
262
360
  const pkCond = core_1.Utils.flatten(meta.primaryKeys.map(pk => meta.properties[pk].fieldNames)).map(pk => `${this.platform.quoteIdentifier(pk)} = ?`).join(' and ');
263
361
  const params = [];
264
362
  let sql = `update ${this.getTableName(meta, options)} set `;
@@ -276,7 +374,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
276
374
  else {
277
375
  sql += '?';
278
376
  }
279
- params.push(...pks, prop.fieldNames.length > 1 ? data[idx][key][fieldNameIdx] : data[idx][key]);
377
+ params.push(...pks, prop.fieldNames.length > 1 ? data[idx][key]?.[fieldNameIdx] : data[idx][key]);
280
378
  }
281
379
  });
282
380
  sql += ` else ${this.platform.quoteIdentifier(fieldName)} end, `;
@@ -300,15 +398,24 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
300
398
  const pks = core_1.Utils.flatten(pkProps.map(pk => meta.properties[pk].fieldNames));
301
399
  sql += pks.length > 1 ? `(${pks.map(pk => `${this.platform.quoteIdentifier(pk)}`).join(', ')})` : this.platform.quoteIdentifier(pks[0]);
302
400
  const conds = where.map(cond => {
401
+ if (core_1.Utils.isPlainObject(cond) && core_1.Utils.getObjectKeysSize(cond) === 1) {
402
+ cond = Object.values(cond)[0];
403
+ }
303
404
  if (pks.length > 1) {
304
- pkProps.forEach(pk => params.push(cond[pk]));
405
+ pkProps.forEach(pk => {
406
+ if (Array.isArray(cond[pk])) {
407
+ params.push(...core_1.Utils.flatten(cond[pk]));
408
+ }
409
+ else {
410
+ params.push(cond[pk]);
411
+ }
412
+ });
305
413
  return `(${new Array(pks.length).fill('?').join(', ')})`;
306
414
  }
307
415
  params.push(cond);
308
416
  return '?';
309
417
  });
310
- const values = pks.length > 1 && this.platform.requiresValuesKeyword() ? 'values ' : '';
311
- sql += ` in (${values}${conds.join(', ')})`;
418
+ sql += ` in (${conds.join(', ')})`;
312
419
  const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx));
313
420
  for (let i = 0; i < collections.length; i++) {
314
421
  await this.processManyToMany(meta, where[i], collections[i], false, options);
@@ -321,17 +428,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
321
428
  if (core_1.Utils.isPrimaryKey(where) && pks.length === 1) {
322
429
  where = { [pks[0]]: where };
323
430
  }
324
- const qb = this.createQueryBuilder(entityName, options.ctx, true, false).delete(where).withSchema(this.getSchemaName(meta, options));
431
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false).delete(where).withSchema(this.getSchemaName(meta, options));
325
432
  return this.rethrow(qb.execute('run', false));
326
433
  }
327
434
  async syncCollection(coll, options) {
328
- const wrapped = coll.owner.__helper;
435
+ const wrapped = (0, core_1.helper)(coll.owner);
329
436
  const meta = wrapped.__meta;
330
437
  const pks = wrapped.getPrimaryKeys(true);
331
438
  const snap = coll.getSnapshot();
332
439
  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));
440
+ const snapshot = snap ? snap.map(item => (0, core_1.helper)(item).getPrimaryKeys(true)) : [];
441
+ const current = coll.getItems(false).map(item => (0, core_1.helper)(item).getPrimaryKeys(true));
335
442
  const deleteDiff = snap ? snapshot.filter(item => !includes(current, item)) : true;
336
443
  const insertDiff = current.filter(item => !includes(snapshot, item));
337
444
  const target = snapshot.filter(item => includes(current, item)).concat(...insertDiff);
@@ -343,9 +450,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
343
450
  deleteDiff.push(...snapshot);
344
451
  insertDiff.push(...current);
345
452
  }
346
- if (coll.property.reference === core_1.ReferenceType.ONE_TO_MANY) {
453
+ if (coll.property.kind === core_1.ReferenceKind.ONE_TO_MANY) {
347
454
  const cols = coll.property.referencedColumnNames;
348
- const qb = this.createQueryBuilder(coll.property.type, ctx, true)
455
+ const qb = this.createQueryBuilder(coll.property.type, ctx, 'write')
349
456
  .withSchema(this.getSchemaName(meta, options))
350
457
  .update({ [coll.property.mappedBy]: pks })
351
458
  .getKnexQuery()
@@ -354,10 +461,10 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
354
461
  }
355
462
  /* istanbul ignore next */
356
463
  const ownerSchema = wrapped.getSchema() === '*' ? this.config.get('schema') : wrapped.getSchema();
357
- const pivotMeta = this.metadata.find(coll.property.pivotTable);
464
+ const pivotMeta = this.metadata.find(coll.property.pivotEntity);
358
465
  if (pivotMeta.schema === '*') {
359
466
  /* istanbul ignore next */
360
- options ?? (options = {});
467
+ options ??= {};
361
468
  options.schema = ownerSchema;
362
469
  }
363
470
  return this.rethrow(this.updateCollectionDiff(meta, coll.property, pks, deleteDiff, insertDiff, options));
@@ -365,7 +472,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
365
472
  async loadFromPivotTable(prop, owners, where = {}, orderBy, ctx, options) {
366
473
  const pivotProp2 = this.getPivotInverseProperty(prop);
367
474
  const ownerMeta = this.metadata.find(pivotProp2.type);
368
- const cond = { [`${prop.pivotTable}.${pivotProp2.name}`]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) } };
475
+ const pivotMeta = this.metadata.find(prop.pivotEntity);
476
+ const cond = { [`${prop.pivotEntity}.${pivotProp2.name}`]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) } };
369
477
  /* istanbul ignore if */
370
478
  if (!core_1.Utils.isEmpty(where) && Object.keys(where).every(k => core_1.Utils.isOperator(k, false))) {
371
479
  where = cond;
@@ -374,15 +482,23 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
374
482
  where = { ...where, ...cond };
375
483
  }
376
484
  orderBy = this.getPivotOrderBy(prop, orderBy);
377
- const qb = this.createQueryBuilder(prop.type, ctx, !!ctx)
485
+ const qb = this.createQueryBuilder(prop.type, ctx, options?.connectionType)
378
486
  .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES)
379
487
  .withSchema(this.getSchemaName(prop.targetMeta, options));
380
- const populate = this.autoJoinOneToOneOwner(prop.targetMeta, [{ field: prop.pivotTable }]);
488
+ const populate = this.autoJoinOneToOneOwner(prop.targetMeta, [{ field: prop.pivotEntity }]);
381
489
  const fields = this.buildFields(prop.targetMeta, (options?.populate ?? []), [], qb, options?.fields);
382
490
  qb.select(fields).populate(populate).where(where).orderBy(orderBy).setLockMode(options?.lockMode, options?.lockTableAliases);
383
491
  if (owners.length === 1 && (options?.offset != null || options?.limit != null)) {
384
492
  qb.limit(options.limit, options.offset);
385
493
  }
494
+ if (prop.targetMeta.schema !== '*' && pivotMeta.schema === '*' && options?.schema) {
495
+ // eslint-disable-next-line dot-notation
496
+ qb['finalize']();
497
+ // eslint-disable-next-line dot-notation
498
+ Object.values(qb['_joins']).forEach(join => {
499
+ join.schema = options.schema;
500
+ });
501
+ }
386
502
  const items = owners.length ? await this.rethrow(qb.execute('all')) : [];
387
503
  const map = {};
388
504
  const pkProps = ownerMeta.getPrimaryProps();
@@ -407,33 +523,24 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
407
523
  async execute(queryOrKnex, params = [], method = 'all', ctx) {
408
524
  return this.rethrow(this.connection.execute(queryOrKnex, params, method, ctx));
409
525
  }
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
526
  /**
421
527
  * 1:1 owner side needs to be marked for population so QB auto-joins the owner id
422
528
  */
423
529
  autoJoinOneToOneOwner(meta, populate, fields = []) {
424
- if (!this.config.get('autoJoinOneToOneOwner') || fields.length > 0) {
530
+ if (!this.config.get('autoJoinOneToOneOwner')) {
425
531
  return populate;
426
532
  }
427
533
  const relationsToPopulate = populate.map(({ field }) => field);
428
534
  const toPopulate = meta.relations
429
- .filter(prop => prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
535
+ .filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
536
+ .filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
430
537
  .map(prop => ({ field: prop.name, strategy: prop.strategy }));
431
538
  return [...populate, ...toPopulate];
432
539
  }
433
540
  joinedProps(meta, populate) {
434
541
  return populate.filter(p => {
435
542
  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;
543
+ return (p.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && prop.kind !== core_1.ReferenceKind.SCALAR;
437
544
  });
438
545
  }
439
546
  /**
@@ -458,12 +565,15 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
458
565
  getFieldsForJoinedLoad(qb, meta, explicitFields, populate = [], parentTableAlias, parentJoinPath) {
459
566
  const fields = [];
460
567
  const joinedProps = this.joinedProps(meta, populate);
568
+ if (explicitFields?.includes('*')) {
569
+ fields.push('*');
570
+ }
461
571
  const shouldHaveColumn = (prop, populate, fields) => {
462
572
  if (!this.platform.shouldHaveColumn(prop, populate)) {
463
573
  return false;
464
574
  }
465
575
  if (!fields || prop.primary) {
466
- return true;
576
+ return !fields?.includes('*');
467
577
  }
468
578
  return fields.includes(prop.name);
469
579
  };
@@ -506,20 +616,34 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
506
616
  }
507
617
  return prop.fieldNames;
508
618
  }
509
- createQueryBuilder(entityName, ctx, write, convertCustomTypes) {
510
- const qb = new QueryBuilder_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, write ? 'write' : 'read');
619
+ /** @internal */
620
+ createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes) {
621
+ const connectionType = this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
622
+ const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType);
511
623
  if (!convertCustomTypes) {
512
624
  qb.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
513
625
  }
514
626
  return qb;
515
627
  }
628
+ resolveConnectionType(args) {
629
+ if (args.ctx) {
630
+ return 'write';
631
+ }
632
+ else if (args.connectionType) {
633
+ return args.connectionType;
634
+ }
635
+ else if (this.config.get('preferReadReplicas') === true) {
636
+ return 'read';
637
+ }
638
+ return 'write';
639
+ }
516
640
  extractManyToMany(entityName, data) {
517
641
  if (!this.metadata.has(entityName)) {
518
642
  return {};
519
643
  }
520
644
  const ret = {};
521
645
  this.metadata.find(entityName).relations.forEach(prop => {
522
- if (prop.reference === core_1.ReferenceType.MANY_TO_MANY && data[prop.name]) {
646
+ if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && data[prop.name]) {
523
647
  ret[prop.name] = data[prop.name].map((item) => core_1.Utils.asArray(item));
524
648
  delete data[prop.name];
525
649
  }
@@ -541,7 +665,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
541
665
  deleteDiff = [];
542
666
  }
543
667
  if (deleteDiff === true || deleteDiff.length > 0) {
544
- const qb1 = this.createQueryBuilder(prop.pivotTable, options?.ctx, true)
668
+ const qb1 = this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
545
669
  .withSchema(this.getSchemaName(meta, options))
546
670
  .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
547
671
  const knex = qb1.getKnex();
@@ -562,11 +686,15 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
562
686
  });
563
687
  /* istanbul ignore else */
564
688
  if (this.platform.allowsMultiInsert()) {
565
- await this.nativeInsertMany(prop.pivotTable, items, { ...options, convertCustomTypes: false, processCollections: false });
689
+ await this.nativeInsertMany(prop.pivotEntity, items, {
690
+ ...options,
691
+ convertCustomTypes: false,
692
+ processCollections: false,
693
+ });
566
694
  }
567
695
  else {
568
696
  await core_1.Utils.runSerial(items, item => {
569
- return this.createQueryBuilder(prop.pivotTable, options?.ctx, true)
697
+ return this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
570
698
  .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES)
571
699
  .withSchema(this.getSchemaName(meta, options))
572
700
  .insert(item)
@@ -575,7 +703,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
575
703
  }
576
704
  }
577
705
  async lockPessimistic(entity, options) {
578
- const meta = entity.__helper.__meta;
706
+ const meta = (0, core_1.helper)(entity).__meta;
579
707
  const qb = this.createQueryBuilder(entity.constructor.name, options.ctx).unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES).withSchema(options.schema ?? meta.schema);
580
708
  const cond = core_1.Utils.getPrimaryKeyCond(entity, meta.primaryKeys);
581
709
  qb.select('1').where(cond).setLockMode(options.lockMode, options.lockTableAliases);
@@ -590,7 +718,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
590
718
  const path = `${parentPath ? parentPath : entityName}.${relation.field}`;
591
719
  const propAlias = qb.getAliasForJoinPath(path);
592
720
  if (propOrderBy) {
593
- Object.keys(propOrderBy).forEach(field => {
721
+ core_1.Utils.keys(propOrderBy).forEach(field => {
594
722
  orderBy.push({ [`${propAlias}.${field}`]: propOrderBy[field] });
595
723
  });
596
724
  }
@@ -601,6 +729,43 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
601
729
  });
602
730
  return orderBy;
603
731
  }
732
+ normalizeFields(fields, prefix = '') {
733
+ const ret = [];
734
+ for (const field of fields) {
735
+ if (typeof field === 'string') {
736
+ ret.push(prefix + field);
737
+ continue;
738
+ }
739
+ if (core_1.Utils.isPlainObject(field)) {
740
+ for (const key of Object.keys(field)) {
741
+ ret.push(...this.normalizeFields(field[key], key + '.'));
742
+ }
743
+ }
744
+ }
745
+ return ret;
746
+ }
747
+ processField(meta, prop, field, ret, populate, joinedProps, qb) {
748
+ if (!prop || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner)) {
749
+ return;
750
+ }
751
+ if (prop.kind === core_1.ReferenceKind.EMBEDDED) {
752
+ if (prop.object) {
753
+ ret.push(prop.fieldNames[0]);
754
+ return;
755
+ }
756
+ const parts = field.split('.');
757
+ const top = parts.shift();
758
+ for (const key of Object.keys(prop.embeddedProps)) {
759
+ if (!top || key === top) {
760
+ this.processField(meta, prop.embeddedProps[key], parts.join('.'), ret, populate, joinedProps, qb);
761
+ }
762
+ }
763
+ return;
764
+ }
765
+ if (prop.fieldNames) {
766
+ ret.push(...prop.fieldNames);
767
+ }
768
+ }
604
769
  buildFields(meta, populate, joinedProps, qb, fields) {
605
770
  const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => p.field === prop.name || p.all));
606
771
  const hasLazyFormulas = meta.props.some(p => p.lazy && p.formula);
@@ -611,11 +776,21 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
611
776
  ret.push(...this.getFieldsForJoinedLoad(qb, meta, fields, populate));
612
777
  }
613
778
  else if (fields) {
614
- for (const field of [...fields]) {
615
- if (core_1.Utils.isPlainObject(field) || field.toString().includes('.')) {
779
+ for (const field of this.normalizeFields(fields)) {
780
+ if (field === '*') {
781
+ ret.push('*');
616
782
  continue;
617
783
  }
618
- ret.push(field);
784
+ const parts = field.split('.');
785
+ const rootPropName = parts.shift(); // first one is the `prop`
786
+ const prop = core_1.QueryHelper.findProperty(rootPropName, {
787
+ metadata: this.metadata,
788
+ platform: this.platform,
789
+ entityName: meta.className,
790
+ where: {},
791
+ aliasMap: qb.getAliasMap(),
792
+ });
793
+ this.processField(meta, prop, parts.join('.'), ret, populate, joinedProps, qb);
619
794
  }
620
795
  ret.unshift(...meta.primaryKeys.filter(pk => !fields.includes(pk)));
621
796
  }
@@ -638,7 +813,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
638
813
  .filter(prop => prop.customType?.convertToDatabaseValueSQL || prop.customType?.convertToJSValueSQL)
639
814
  .forEach(prop => ret.push(prop.name));
640
815
  }
641
- return ret.length > 0 ? ret : ['*'];
816
+ return ret.length > 0 ? core_1.Utils.unique(ret) : ['*'];
642
817
  }
643
818
  }
644
819
  exports.AbstractSqlDriver = AbstractSqlDriver;