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

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.js → AbstractSqlConnection.js} +6 -1
  2. package/AbstractSqlDriver.d.ts +66 -0
  3. package/{dist/AbstractSqlDriver.js → AbstractSqlDriver.js} +277 -95
  4. package/{dist/AbstractSqlPlatform.d.ts → AbstractSqlPlatform.d.ts} +7 -7
  5. package/{dist/AbstractSqlPlatform.js → AbstractSqlPlatform.js} +12 -14
  6. package/{dist/README.md → README.md} +2 -2
  7. package/{dist/SqlEntityManager.d.ts → SqlEntityManager.d.ts} +5 -5
  8. package/{dist/SqlEntityManager.js → SqlEntityManager.js} +1 -2
  9. package/{dist/SqlEntityRepository.d.ts → SqlEntityRepository.d.ts} +3 -4
  10. package/{dist/SqlEntityRepository.js → SqlEntityRepository.js} +1 -2
  11. package/{dist/index.d.ts → index.d.ts} +1 -0
  12. package/{dist/index.js → index.js} +6 -1
  13. package/index.mjs +194 -0
  14. package/package.json +18 -47
  15. package/query/ArrayCriteriaNode.d.ts +9 -0
  16. package/{dist/query → query}/CriteriaNode.d.ts +8 -8
  17. package/{dist/query → query}/CriteriaNode.js +15 -12
  18. package/query/CriteriaNodeFactory.d.ts +12 -0
  19. package/{dist/query → query}/CriteriaNodeFactory.js +10 -10
  20. package/{dist/query → query}/ObjectCriteriaNode.d.ts +3 -3
  21. package/{dist/query → query}/ObjectCriteriaNode.js +19 -6
  22. package/{dist/query → query}/QueryBuilder.d.ts +66 -24
  23. package/{dist/query → query}/QueryBuilder.js +381 -94
  24. package/{dist/query → query}/QueryBuilderHelper.d.ts +19 -11
  25. package/{dist/query → query}/QueryBuilderHelper.js +227 -116
  26. package/{dist/query → query}/ScalarCriteriaNode.d.ts +2 -2
  27. package/{dist/query → query}/ScalarCriteriaNode.js +6 -6
  28. package/{dist/query → query}/index.js +5 -1
  29. package/{dist/schema → schema}/DatabaseSchema.d.ts +1 -1
  30. package/{dist/schema → schema}/DatabaseSchema.js +11 -19
  31. package/{dist/schema → schema}/DatabaseTable.d.ts +9 -9
  32. package/{dist/schema → schema}/DatabaseTable.js +22 -22
  33. package/{dist/schema → schema}/SchemaComparator.d.ts +7 -5
  34. package/{dist/schema → schema}/SchemaComparator.js +60 -10
  35. package/{dist/schema → schema}/SchemaHelper.d.ts +15 -10
  36. package/{dist/schema → schema}/SchemaHelper.js +46 -8
  37. package/{dist/schema/SchemaGenerator.d.ts → schema/SqlSchemaGenerator.d.ts} +10 -12
  38. package/{dist/schema/SchemaGenerator.js → schema/SqlSchemaGenerator.js} +89 -45
  39. package/{dist/schema → schema}/index.d.ts +1 -1
  40. package/{dist/schema → schema}/index.js +6 -2
  41. package/{dist/typings.d.ts → typings.d.ts} +21 -16
  42. package/CHANGELOG.md +0 -661
  43. package/dist/AbstractSqlDriver.d.ts +0 -55
  44. package/dist/LICENSE +0 -21
  45. package/dist/index.mjs +0 -26
  46. package/dist/package.json +0 -99
  47. package/dist/query/ArrayCriteriaNode.d.ts +0 -9
  48. package/dist/query/CriteriaNodeFactory.d.ts +0 -12
  49. /package/{dist/AbstractSqlConnection.d.ts → AbstractSqlConnection.d.ts} +0 -0
  50. /package/{dist/MonkeyPatchable.d.ts → MonkeyPatchable.d.ts} +0 -0
  51. /package/{dist/MonkeyPatchable.js → MonkeyPatchable.js} +0 -0
  52. /package/{dist/query → query}/ArrayCriteriaNode.js +0 -0
  53. /package/{dist/query → query}/enums.d.ts +0 -0
  54. /package/{dist/query → query}/enums.js +0 -0
  55. /package/{dist/query → query}/index.d.ts +0 -0
  56. /package/{dist/typings.js → typings.js} +0 -0
@@ -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,68 +239,77 @@ 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
+ const addParams = (prop, row) => {
271
+ if (options.convertCustomTypes && prop.customType) {
272
+ params.push(prop.customType.convertToDatabaseValue(row[prop.name], this.platform, { key: prop.name, mode: 'query' }));
273
+ return;
274
+ }
275
+ params.push(row[prop.name]);
276
+ };
277
+ if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
191
278
  sql += data.map(row => {
192
279
  const keys = [];
193
280
  props.forEach(prop => {
194
281
  if (prop.fieldNames.length > 1) {
195
- params.push(...row[prop.name]);
196
- keys.push(...prop.fieldNames.map(_ => '?'));
282
+ params.push(...(row[prop.name] ?? prop.fieldNames.map(() => null)));
283
+ keys.push(...(row[prop.name] ?? prop.fieldNames).map(() => '?'));
197
284
  }
198
285
  else if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(row[prop.name])) {
199
286
  keys.push(prop.customType.convertToDatabaseValueSQL('?', this.platform));
200
- params.push(row[prop.name]);
287
+ addParams(prop, row);
201
288
  }
202
289
  else {
203
- params.push(row[prop.name]);
290
+ addParams(prop, row);
204
291
  keys.push('?');
205
292
  }
206
293
  });
207
- return '(' + keys.join(', ') + ')';
294
+ return '(' + (keys.join(', ') || 'default') + ')';
208
295
  }).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
296
  }
297
+ if (this.platform.usesReturningStatement()) {
298
+ /* istanbul ignore next */
299
+ const returningProps = meta.hydrateProps.filter(prop => prop.persist !== false && (prop.primary || prop.defaultRaw));
300
+ const returningFields = core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames));
301
+ /* istanbul ignore next */
302
+ sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
303
+ }
304
+ const res = await this.execute(sql, params, 'run', options.ctx);
218
305
  let pk;
219
306
  /* istanbul ignore next */
220
307
  if (pks.length > 1) { // owner has composite pk
221
308
  pk = data.map(d => core_1.Utils.getPrimaryKeyCond(d, pks));
222
309
  }
223
310
  else {
224
- res.row ?? (res.row = {});
225
- res.rows ?? (res.rows = []);
311
+ res.row ??= {};
312
+ res.rows ??= [];
226
313
  pk = data.map((d, i) => d[pks[0]] ?? res.rows[i]?.[pks[0]]).map(d => [d]);
227
314
  res.insertId = res.insertId || res.row[pks[0]];
228
315
  }
@@ -232,19 +319,29 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
232
319
  return res;
233
320
  }
234
321
  async nativeUpdate(entityName, where, data, options = {}) {
235
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
322
+ options.convertCustomTypes ??= true;
236
323
  const meta = this.metadata.find(entityName);
237
324
  const pks = this.getPrimaryKeyFields(entityName);
238
325
  const collections = this.extractManyToMany(entityName, data);
239
326
  let res = { affectedRows: 0, insertId: 0, row: {} };
240
327
  if (core_1.Utils.isPrimaryKey(where) && pks.length === 1) {
241
- where = { [pks[0]]: where };
328
+ /* istanbul ignore next */
329
+ where = { [meta?.primaryKeys[0] ?? pks[0]]: where };
242
330
  }
243
331
  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);
332
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes)
333
+ .withSchema(this.getSchemaName(meta, options));
334
+ if (options.upsert) {
335
+ /* istanbul ignore next */
336
+ const uniqueFields = core_1.Utils.isPlainObject(where) ? core_1.Utils.keys(where) : meta.primaryKeys;
337
+ /* istanbul ignore next */
338
+ qb.insert(data)
339
+ .onConflict(uniqueFields.map(p => meta?.properties[p]?.fieldNames[0] ?? p))
340
+ .merge(core_1.Utils.keys(data).filter(f => !uniqueFields.includes(f)));
341
+ }
342
+ else {
343
+ qb.update(data).where(where);
344
+ }
248
345
  res = await this.rethrow(qb.execute('run', false));
249
346
  }
250
347
  /* istanbul ignore next */
@@ -253,12 +350,20 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
253
350
  return res;
254
351
  }
255
352
  async nativeUpdateMany(entityName, where, data, options = {}) {
256
- options.processCollections ?? (options.processCollections = true);
257
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
353
+ options.processCollections ??= true;
354
+ options.convertCustomTypes ??= true;
258
355
  const meta = this.metadata.get(entityName);
356
+ if (options.upsert) {
357
+ const uniqueFields = core_1.Utils.isPlainObject(where[0]) ? Object.keys(where[0]) : meta.primaryKeys;
358
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
359
+ qb.insert(data)
360
+ .onConflict(uniqueFields.map(p => meta?.properties[p]?.fieldNames[0] ?? p))
361
+ .merge(Object.keys(data[0]).filter(f => !uniqueFields.includes(f)));
362
+ return qb.execute('run', false);
363
+ }
259
364
  const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
260
365
  const keys = new Set();
261
- data.forEach(row => Object.keys(row).forEach(k => keys.add(k)));
366
+ data.forEach(row => core_1.Utils.keys(row).forEach(k => keys.add(k)));
262
367
  const pkCond = core_1.Utils.flatten(meta.primaryKeys.map(pk => meta.properties[pk].fieldNames)).map(pk => `${this.platform.quoteIdentifier(pk)} = ?`).join(' and ');
263
368
  const params = [];
264
369
  let sql = `update ${this.getTableName(meta, options)} set `;
@@ -276,7 +381,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
276
381
  else {
277
382
  sql += '?';
278
383
  }
279
- params.push(...pks, prop.fieldNames.length > 1 ? data[idx][key][fieldNameIdx] : data[idx][key]);
384
+ params.push(...pks, prop.fieldNames.length > 1 ? data[idx][key]?.[fieldNameIdx] : data[idx][key]);
280
385
  }
281
386
  });
282
387
  sql += ` else ${this.platform.quoteIdentifier(fieldName)} end, `;
@@ -300,15 +405,24 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
300
405
  const pks = core_1.Utils.flatten(pkProps.map(pk => meta.properties[pk].fieldNames));
301
406
  sql += pks.length > 1 ? `(${pks.map(pk => `${this.platform.quoteIdentifier(pk)}`).join(', ')})` : this.platform.quoteIdentifier(pks[0]);
302
407
  const conds = where.map(cond => {
408
+ if (core_1.Utils.isPlainObject(cond) && core_1.Utils.getObjectKeysSize(cond) === 1) {
409
+ cond = Object.values(cond)[0];
410
+ }
303
411
  if (pks.length > 1) {
304
- pkProps.forEach(pk => params.push(cond[pk]));
412
+ pkProps.forEach(pk => {
413
+ if (Array.isArray(cond[pk])) {
414
+ params.push(...core_1.Utils.flatten(cond[pk]));
415
+ }
416
+ else {
417
+ params.push(cond[pk]);
418
+ }
419
+ });
305
420
  return `(${new Array(pks.length).fill('?').join(', ')})`;
306
421
  }
307
422
  params.push(cond);
308
423
  return '?';
309
424
  });
310
- const values = pks.length > 1 && this.platform.requiresValuesKeyword() ? 'values ' : '';
311
- sql += ` in (${values}${conds.join(', ')})`;
425
+ sql += ` in (${conds.join(', ')})`;
312
426
  const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx));
313
427
  for (let i = 0; i < collections.length; i++) {
314
428
  await this.processManyToMany(meta, where[i], collections[i], false, options);
@@ -321,17 +435,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
321
435
  if (core_1.Utils.isPrimaryKey(where) && pks.length === 1) {
322
436
  where = { [pks[0]]: where };
323
437
  }
324
- const qb = this.createQueryBuilder(entityName, options.ctx, true, false).delete(where).withSchema(this.getSchemaName(meta, options));
438
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false).delete(where).withSchema(this.getSchemaName(meta, options));
325
439
  return this.rethrow(qb.execute('run', false));
326
440
  }
327
441
  async syncCollection(coll, options) {
328
- const wrapped = coll.owner.__helper;
442
+ const wrapped = (0, core_1.helper)(coll.owner);
329
443
  const meta = wrapped.__meta;
330
444
  const pks = wrapped.getPrimaryKeys(true);
331
445
  const snap = coll.getSnapshot();
332
446
  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));
447
+ const snapshot = snap ? snap.map(item => (0, core_1.helper)(item).getPrimaryKeys(true)) : [];
448
+ const current = coll.getItems(false).map(item => (0, core_1.helper)(item).getPrimaryKeys(true));
335
449
  const deleteDiff = snap ? snapshot.filter(item => !includes(current, item)) : true;
336
450
  const insertDiff = current.filter(item => !includes(snapshot, item));
337
451
  const target = snapshot.filter(item => includes(current, item)).concat(...insertDiff);
@@ -343,9 +457,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
343
457
  deleteDiff.push(...snapshot);
344
458
  insertDiff.push(...current);
345
459
  }
346
- if (coll.property.reference === core_1.ReferenceType.ONE_TO_MANY) {
460
+ if (coll.property.kind === core_1.ReferenceKind.ONE_TO_MANY) {
347
461
  const cols = coll.property.referencedColumnNames;
348
- const qb = this.createQueryBuilder(coll.property.type, ctx, true)
462
+ const qb = this.createQueryBuilder(coll.property.type, ctx, 'write')
349
463
  .withSchema(this.getSchemaName(meta, options))
350
464
  .update({ [coll.property.mappedBy]: pks })
351
465
  .getKnexQuery()
@@ -354,10 +468,10 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
354
468
  }
355
469
  /* istanbul ignore next */
356
470
  const ownerSchema = wrapped.getSchema() === '*' ? this.config.get('schema') : wrapped.getSchema();
357
- const pivotMeta = this.metadata.find(coll.property.pivotTable);
471
+ const pivotMeta = this.metadata.find(coll.property.pivotEntity);
358
472
  if (pivotMeta.schema === '*') {
359
473
  /* istanbul ignore next */
360
- options ?? (options = {});
474
+ options ??= {};
361
475
  options.schema = ownerSchema;
362
476
  }
363
477
  return this.rethrow(this.updateCollectionDiff(meta, coll.property, pks, deleteDiff, insertDiff, options));
@@ -365,7 +479,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
365
479
  async loadFromPivotTable(prop, owners, where = {}, orderBy, ctx, options) {
366
480
  const pivotProp2 = this.getPivotInverseProperty(prop);
367
481
  const ownerMeta = this.metadata.find(pivotProp2.type);
368
- const cond = { [`${prop.pivotTable}.${pivotProp2.name}`]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) } };
482
+ const pivotMeta = this.metadata.find(prop.pivotEntity);
483
+ const cond = { [`${prop.pivotEntity}.${pivotProp2.name}`]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) } };
369
484
  /* istanbul ignore if */
370
485
  if (!core_1.Utils.isEmpty(where) && Object.keys(where).every(k => core_1.Utils.isOperator(k, false))) {
371
486
  where = cond;
@@ -374,15 +489,23 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
374
489
  where = { ...where, ...cond };
375
490
  }
376
491
  orderBy = this.getPivotOrderBy(prop, orderBy);
377
- const qb = this.createQueryBuilder(prop.type, ctx, !!ctx)
492
+ const qb = this.createQueryBuilder(prop.type, ctx, options?.connectionType)
378
493
  .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES)
379
494
  .withSchema(this.getSchemaName(prop.targetMeta, options));
380
- const populate = this.autoJoinOneToOneOwner(prop.targetMeta, [{ field: prop.pivotTable }]);
495
+ const populate = this.autoJoinOneToOneOwner(prop.targetMeta, [{ field: prop.pivotEntity }]);
381
496
  const fields = this.buildFields(prop.targetMeta, (options?.populate ?? []), [], qb, options?.fields);
382
497
  qb.select(fields).populate(populate).where(where).orderBy(orderBy).setLockMode(options?.lockMode, options?.lockTableAliases);
383
498
  if (owners.length === 1 && (options?.offset != null || options?.limit != null)) {
384
499
  qb.limit(options.limit, options.offset);
385
500
  }
501
+ if (prop.targetMeta.schema !== '*' && pivotMeta.schema === '*' && options?.schema) {
502
+ // eslint-disable-next-line dot-notation
503
+ qb['finalize']();
504
+ // eslint-disable-next-line dot-notation
505
+ Object.values(qb['_joins']).forEach(join => {
506
+ join.schema = options.schema;
507
+ });
508
+ }
386
509
  const items = owners.length ? await this.rethrow(qb.execute('all')) : [];
387
510
  const map = {};
388
511
  const pkProps = ownerMeta.getPrimaryProps();
@@ -407,33 +530,24 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
407
530
  async execute(queryOrKnex, params = [], method = 'all', ctx) {
408
531
  return this.rethrow(this.connection.execute(queryOrKnex, params, method, ctx));
409
532
  }
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
533
  /**
421
534
  * 1:1 owner side needs to be marked for population so QB auto-joins the owner id
422
535
  */
423
536
  autoJoinOneToOneOwner(meta, populate, fields = []) {
424
- if (!this.config.get('autoJoinOneToOneOwner') || fields.length > 0) {
537
+ if (!this.config.get('autoJoinOneToOneOwner')) {
425
538
  return populate;
426
539
  }
427
540
  const relationsToPopulate = populate.map(({ field }) => field);
428
541
  const toPopulate = meta.relations
429
- .filter(prop => prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
542
+ .filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
543
+ .filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
430
544
  .map(prop => ({ field: prop.name, strategy: prop.strategy }));
431
545
  return [...populate, ...toPopulate];
432
546
  }
433
547
  joinedProps(meta, populate) {
434
548
  return populate.filter(p => {
435
549
  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;
550
+ return (p.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && prop.kind !== core_1.ReferenceKind.SCALAR;
437
551
  });
438
552
  }
439
553
  /**
@@ -458,12 +572,15 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
458
572
  getFieldsForJoinedLoad(qb, meta, explicitFields, populate = [], parentTableAlias, parentJoinPath) {
459
573
  const fields = [];
460
574
  const joinedProps = this.joinedProps(meta, populate);
575
+ if (explicitFields?.includes('*')) {
576
+ fields.push('*');
577
+ }
461
578
  const shouldHaveColumn = (prop, populate, fields) => {
462
579
  if (!this.platform.shouldHaveColumn(prop, populate)) {
463
580
  return false;
464
581
  }
465
582
  if (!fields || prop.primary) {
466
- return true;
583
+ return !fields?.includes('*');
467
584
  }
468
585
  return fields.includes(prop.name);
469
586
  };
@@ -506,20 +623,34 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
506
623
  }
507
624
  return prop.fieldNames;
508
625
  }
509
- createQueryBuilder(entityName, ctx, write, convertCustomTypes) {
510
- const qb = new QueryBuilder_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, write ? 'write' : 'read');
626
+ /** @internal */
627
+ createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes) {
628
+ const connectionType = this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
629
+ const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType);
511
630
  if (!convertCustomTypes) {
512
631
  qb.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
513
632
  }
514
633
  return qb;
515
634
  }
635
+ resolveConnectionType(args) {
636
+ if (args.ctx) {
637
+ return 'write';
638
+ }
639
+ else if (args.connectionType) {
640
+ return args.connectionType;
641
+ }
642
+ else if (this.config.get('preferReadReplicas') === true) {
643
+ return 'read';
644
+ }
645
+ return 'write';
646
+ }
516
647
  extractManyToMany(entityName, data) {
517
648
  if (!this.metadata.has(entityName)) {
518
649
  return {};
519
650
  }
520
651
  const ret = {};
521
652
  this.metadata.find(entityName).relations.forEach(prop => {
522
- if (prop.reference === core_1.ReferenceType.MANY_TO_MANY && data[prop.name]) {
653
+ if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && data[prop.name]) {
523
654
  ret[prop.name] = data[prop.name].map((item) => core_1.Utils.asArray(item));
524
655
  delete data[prop.name];
525
656
  }
@@ -541,7 +672,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
541
672
  deleteDiff = [];
542
673
  }
543
674
  if (deleteDiff === true || deleteDiff.length > 0) {
544
- const qb1 = this.createQueryBuilder(prop.pivotTable, options?.ctx, true)
675
+ const qb1 = this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
545
676
  .withSchema(this.getSchemaName(meta, options))
546
677
  .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
547
678
  const knex = qb1.getKnex();
@@ -562,11 +693,15 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
562
693
  });
563
694
  /* istanbul ignore else */
564
695
  if (this.platform.allowsMultiInsert()) {
565
- await this.nativeInsertMany(prop.pivotTable, items, { ...options, convertCustomTypes: false, processCollections: false });
696
+ await this.nativeInsertMany(prop.pivotEntity, items, {
697
+ ...options,
698
+ convertCustomTypes: false,
699
+ processCollections: false,
700
+ });
566
701
  }
567
702
  else {
568
703
  await core_1.Utils.runSerial(items, item => {
569
- return this.createQueryBuilder(prop.pivotTable, options?.ctx, true)
704
+ return this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
570
705
  .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES)
571
706
  .withSchema(this.getSchemaName(meta, options))
572
707
  .insert(item)
@@ -575,7 +710,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
575
710
  }
576
711
  }
577
712
  async lockPessimistic(entity, options) {
578
- const meta = entity.__helper.__meta;
713
+ const meta = (0, core_1.helper)(entity).__meta;
579
714
  const qb = this.createQueryBuilder(entity.constructor.name, options.ctx).unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES).withSchema(options.schema ?? meta.schema);
580
715
  const cond = core_1.Utils.getPrimaryKeyCond(entity, meta.primaryKeys);
581
716
  qb.select('1').where(cond).setLockMode(options.lockMode, options.lockTableAliases);
@@ -590,7 +725,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
590
725
  const path = `${parentPath ? parentPath : entityName}.${relation.field}`;
591
726
  const propAlias = qb.getAliasForJoinPath(path);
592
727
  if (propOrderBy) {
593
- Object.keys(propOrderBy).forEach(field => {
728
+ core_1.Utils.keys(propOrderBy).forEach(field => {
594
729
  orderBy.push({ [`${propAlias}.${field}`]: propOrderBy[field] });
595
730
  });
596
731
  }
@@ -601,6 +736,43 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
601
736
  });
602
737
  return orderBy;
603
738
  }
739
+ normalizeFields(fields, prefix = '') {
740
+ const ret = [];
741
+ for (const field of fields) {
742
+ if (typeof field === 'string') {
743
+ ret.push(prefix + field);
744
+ continue;
745
+ }
746
+ if (core_1.Utils.isPlainObject(field)) {
747
+ for (const key of Object.keys(field)) {
748
+ ret.push(...this.normalizeFields(field[key], key + '.'));
749
+ }
750
+ }
751
+ }
752
+ return ret;
753
+ }
754
+ processField(meta, prop, field, ret, populate, joinedProps, qb) {
755
+ if (!prop || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner)) {
756
+ return;
757
+ }
758
+ if (prop.kind === core_1.ReferenceKind.EMBEDDED) {
759
+ if (prop.object) {
760
+ ret.push(prop.fieldNames[0]);
761
+ return;
762
+ }
763
+ const parts = field.split('.');
764
+ const top = parts.shift();
765
+ for (const key of Object.keys(prop.embeddedProps)) {
766
+ if (!top || key === top) {
767
+ this.processField(meta, prop.embeddedProps[key], parts.join('.'), ret, populate, joinedProps, qb);
768
+ }
769
+ }
770
+ return;
771
+ }
772
+ if (prop.fieldNames) {
773
+ ret.push(...prop.fieldNames);
774
+ }
775
+ }
604
776
  buildFields(meta, populate, joinedProps, qb, fields) {
605
777
  const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => p.field === prop.name || p.all));
606
778
  const hasLazyFormulas = meta.props.some(p => p.lazy && p.formula);
@@ -611,11 +783,21 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
611
783
  ret.push(...this.getFieldsForJoinedLoad(qb, meta, fields, populate));
612
784
  }
613
785
  else if (fields) {
614
- for (const field of [...fields]) {
615
- if (core_1.Utils.isPlainObject(field) || field.toString().includes('.')) {
786
+ for (const field of this.normalizeFields(fields)) {
787
+ if (field === '*') {
788
+ ret.push('*');
616
789
  continue;
617
790
  }
618
- ret.push(field);
791
+ const parts = field.split('.');
792
+ const rootPropName = parts.shift(); // first one is the `prop`
793
+ const prop = core_1.QueryHelper.findProperty(rootPropName, {
794
+ metadata: this.metadata,
795
+ platform: this.platform,
796
+ entityName: meta.className,
797
+ where: {},
798
+ aliasMap: qb.getAliasMap(),
799
+ });
800
+ this.processField(meta, prop, parts.join('.'), ret, populate, joinedProps, qb);
619
801
  }
620
802
  ret.unshift(...meta.primaryKeys.filter(pk => !fields.includes(pk)));
621
803
  }
@@ -638,7 +820,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
638
820
  .filter(prop => prop.customType?.convertToDatabaseValueSQL || prop.customType?.convertToJSValueSQL)
639
821
  .forEach(prop => ret.push(prop.name));
640
822
  }
641
- return ret.length > 0 ? ret : ['*'];
823
+ return ret.length > 0 ? core_1.Utils.unique(ret) : ['*'];
642
824
  }
643
825
  }
644
826
  exports.AbstractSqlDriver = AbstractSqlDriver;