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

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 (69) 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 +73 -0
  4. package/AbstractSqlDriver.js +1085 -0
  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/PivotCollectionPersister.d.ts +17 -0
  10. package/PivotCollectionPersister.js +128 -0
  11. package/{dist/README.md → README.md} +66 -109
  12. package/{dist/SqlEntityManager.d.ts → SqlEntityManager.d.ts} +6 -10
  13. package/{dist/SqlEntityManager.js → SqlEntityManager.js} +5 -10
  14. package/SqlEntityRepository.d.ts +24 -0
  15. package/{dist/SqlEntityRepository.js → SqlEntityRepository.js} +11 -8
  16. package/{dist/index.d.ts → index.d.ts} +1 -0
  17. package/{dist/index.js → index.js} +6 -1
  18. package/index.mjs +211 -0
  19. package/package.json +20 -49
  20. package/query/ArrayCriteriaNode.d.ts +10 -0
  21. package/{dist/query → query}/ArrayCriteriaNode.js +7 -2
  22. package/{dist/query → query}/CriteriaNode.d.ts +11 -9
  23. package/{dist/query → query}/CriteriaNode.js +31 -15
  24. package/query/CriteriaNodeFactory.d.ts +12 -0
  25. package/{dist/query → query}/CriteriaNodeFactory.js +32 -15
  26. package/query/ObjectCriteriaNode.d.ts +15 -0
  27. package/{dist/query → query}/ObjectCriteriaNode.js +68 -21
  28. package/query/QueryBuilder.d.ts +284 -0
  29. package/query/QueryBuilder.js +1218 -0
  30. package/{dist/query → query}/QueryBuilderHelper.d.ts +26 -23
  31. package/query/QueryBuilderHelper.js +661 -0
  32. package/query/ScalarCriteriaNode.d.ts +10 -0
  33. package/{dist/query → query}/ScalarCriteriaNode.js +12 -8
  34. package/query/enums.d.ts +15 -0
  35. package/query/enums.js +20 -0
  36. package/{dist/query → query}/index.js +5 -1
  37. package/{dist/schema → schema}/DatabaseSchema.d.ts +6 -3
  38. package/{dist/schema → schema}/DatabaseSchema.js +35 -25
  39. package/{dist/schema → schema}/DatabaseTable.d.ts +21 -13
  40. package/schema/DatabaseTable.js +645 -0
  41. package/{dist/schema → schema}/SchemaComparator.d.ts +9 -7
  42. package/{dist/schema → schema}/SchemaComparator.js +99 -25
  43. package/{dist/schema → schema}/SchemaHelper.d.ts +20 -13
  44. package/{dist/schema → schema}/SchemaHelper.js +70 -18
  45. package/{dist/schema/SchemaGenerator.d.ts → schema/SqlSchemaGenerator.d.ts} +11 -14
  46. package/{dist/schema/SchemaGenerator.js → schema/SqlSchemaGenerator.js} +149 -63
  47. package/{dist/schema → schema}/index.d.ts +1 -1
  48. package/{dist/schema → schema}/index.js +6 -2
  49. package/{dist/typings.d.ts → typings.d.ts} +45 -22
  50. package/CHANGELOG.md +0 -661
  51. package/dist/AbstractSqlDriver.d.ts +0 -55
  52. package/dist/AbstractSqlDriver.js +0 -644
  53. package/dist/AbstractSqlPlatform.d.ts +0 -21
  54. package/dist/LICENSE +0 -21
  55. package/dist/SqlEntityRepository.d.ts +0 -23
  56. package/dist/index.mjs +0 -26
  57. package/dist/package.json +0 -99
  58. package/dist/query/ArrayCriteriaNode.d.ts +0 -9
  59. package/dist/query/CriteriaNodeFactory.d.ts +0 -12
  60. package/dist/query/ObjectCriteriaNode.d.ts +0 -14
  61. package/dist/query/QueryBuilder.d.ts +0 -211
  62. package/dist/query/QueryBuilder.js +0 -688
  63. package/dist/query/QueryBuilderHelper.js +0 -523
  64. package/dist/query/ScalarCriteriaNode.d.ts +0 -9
  65. package/dist/query/enums.d.ts +0 -8
  66. package/dist/query/enums.js +0 -12
  67. package/dist/schema/DatabaseTable.js +0 -319
  68. /package/{dist/query → query}/index.d.ts +0 -0
  69. /package/{dist/typings.js → typings.js} +0 -0
@@ -0,0 +1,1085 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AbstractSqlDriver = void 0;
4
+ const core_1 = require("@mikro-orm/core");
5
+ const query_1 = require("./query");
6
+ const SqlEntityManager_1 = require("./SqlEntityManager");
7
+ const PivotCollectionPersister_1 = require("./PivotCollectionPersister");
8
+ class AbstractSqlDriver extends core_1.DatabaseDriver {
9
+ [core_1.EntityManagerType];
10
+ connection;
11
+ replicas = [];
12
+ platform;
13
+ constructor(config, platform, connection, connector) {
14
+ super(config, connector);
15
+ this.connection = new connection(this.config);
16
+ this.replicas = this.createReplicas(conf => new connection(this.config, conf, 'read'));
17
+ this.platform = platform;
18
+ }
19
+ getPlatform() {
20
+ return this.platform;
21
+ }
22
+ createEntityManager(useContext) {
23
+ return new SqlEntityManager_1.SqlEntityManager(this.config, this, this.metadata, useContext);
24
+ }
25
+ async find(entityName, where, options = {}) {
26
+ options = { populate: [], orderBy: [], ...options };
27
+ const meta = this.metadata.find(entityName);
28
+ if (meta?.virtual) {
29
+ return this.findVirtual(entityName, where, options);
30
+ }
31
+ const populate = this.autoJoinOneToOneOwner(meta, options.populate, options.fields);
32
+ const joinedProps = this.joinedProps(meta, populate, options);
33
+ const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging);
34
+ const fields = this.buildFields(meta, populate, joinedProps, qb, qb.alias, options.fields, options);
35
+ const orderBy = this.buildOrderBy(qb, meta, populate, options);
36
+ core_1.Utils.asArray(options.flags).forEach(flag => qb.setFlag(flag));
37
+ if (core_1.Utils.isPrimaryKey(where, meta.compositePK)) {
38
+ where = { [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
39
+ }
40
+ const { first, last, before, after } = options;
41
+ const isCursorPagination = [first, last, before, after].some(v => v != null);
42
+ qb.__populateWhere = options._populateWhere;
43
+ qb.select(fields)
44
+ // only add populateWhere if we are populate-joining, as this will be used to add `on` conditions
45
+ .populate(populate, joinedProps.length > 0 ? options.populateWhere : undefined)
46
+ .where(where)
47
+ .groupBy(options.groupBy)
48
+ .having(options.having)
49
+ .indexHint(options.indexHint)
50
+ .comment(options.comments)
51
+ .hintComment(options.hintComments)
52
+ .withSchema(this.getSchemaName(meta, options));
53
+ if (isCursorPagination) {
54
+ const { orderBy: newOrderBy, where } = this.processCursorOptions(meta, options, orderBy);
55
+ qb.andWhere(where).orderBy(newOrderBy);
56
+ }
57
+ else {
58
+ qb.orderBy(orderBy);
59
+ }
60
+ if (options.limit != null || options.offset != null) {
61
+ qb.limit(options.limit, options.offset);
62
+ }
63
+ if (options.lockMode) {
64
+ qb.setLockMode(options.lockMode, options.lockTableAliases);
65
+ }
66
+ const result = await this.rethrow(qb.execute('all'));
67
+ if (isCursorPagination && !first && !!last) {
68
+ result.reverse();
69
+ }
70
+ return result;
71
+ }
72
+ async findOne(entityName, where, options) {
73
+ const opts = { populate: [], ...(options || {}) };
74
+ const meta = this.metadata.find(entityName);
75
+ const populate = this.autoJoinOneToOneOwner(meta, opts.populate, opts.fields);
76
+ const joinedProps = this.joinedProps(meta, populate, options);
77
+ const hasToManyJoins = joinedProps.some(hint => {
78
+ const [propName] = hint.field.split(':', 2);
79
+ const prop = meta.properties[propName];
80
+ return prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(prop.kind);
81
+ });
82
+ if (joinedProps.length === 0 || !hasToManyJoins) {
83
+ opts.limit = 1;
84
+ }
85
+ if (opts.limit > 0 && !opts.flags?.includes(core_1.QueryFlag.DISABLE_PAGINATE)) {
86
+ opts.flags ??= [];
87
+ opts.flags.push(core_1.QueryFlag.DISABLE_PAGINATE);
88
+ }
89
+ const res = await this.find(entityName, where, opts);
90
+ return res[0] || null;
91
+ }
92
+ async findVirtual(entityName, where, options) {
93
+ return this.findFromVirtual(entityName, where, options, query_1.QueryType.SELECT);
94
+ }
95
+ async countVirtual(entityName, where, options) {
96
+ return this.findFromVirtual(entityName, where, options, query_1.QueryType.COUNT);
97
+ }
98
+ async findFromVirtual(entityName, where, options, type) {
99
+ const meta = this.metadata.get(entityName);
100
+ /* istanbul ignore next */
101
+ if (!meta.expression) {
102
+ return type === query_1.QueryType.SELECT ? [] : 0;
103
+ }
104
+ if (typeof meta.expression === 'string') {
105
+ return this.wrapVirtualExpressionInSubquery(meta, meta.expression, where, options, type);
106
+ }
107
+ const em = this.createEntityManager(false);
108
+ em.setTransactionContext(options.ctx);
109
+ const res = meta.expression(em, where, options);
110
+ if (typeof res === 'string') {
111
+ return this.wrapVirtualExpressionInSubquery(meta, res, where, options, type);
112
+ }
113
+ if (res instanceof query_1.QueryBuilder) {
114
+ return this.wrapVirtualExpressionInSubquery(meta, res.getFormattedQuery(), where, options, type);
115
+ }
116
+ if (core_1.Utils.isObject(res)) {
117
+ const { sql, bindings } = res.toSQL();
118
+ const query = this.platform.formatQuery(sql, bindings);
119
+ return this.wrapVirtualExpressionInSubquery(meta, query, where, options, type);
120
+ }
121
+ /* istanbul ignore next */
122
+ return res;
123
+ }
124
+ async wrapVirtualExpressionInSubquery(meta, expression, where, options, type) {
125
+ const qb = this.createQueryBuilder(meta.className, options?.ctx, options.connectionType, options.convertCustomTypes)
126
+ .indexHint(options.indexHint)
127
+ .comment(options.comments)
128
+ .hintComment(options.hintComments);
129
+ if (type !== query_1.QueryType.COUNT) {
130
+ qb.limit(options?.limit, options?.offset);
131
+ if (options.orderBy) {
132
+ qb.orderBy(options.orderBy);
133
+ }
134
+ }
135
+ qb.where(where);
136
+ const kqb = qb.getKnexQuery().clear('select');
137
+ if (type === query_1.QueryType.COUNT) {
138
+ kqb.select(this.connection.getKnex().raw('count(*) as count'));
139
+ }
140
+ else { // select
141
+ kqb.select('*');
142
+ }
143
+ kqb.fromRaw(`(${expression}) as ${this.platform.quoteIdentifier(qb.alias)}`);
144
+ const res = await this.execute(kqb);
145
+ if (type === query_1.QueryType.COUNT) {
146
+ return res[0].count;
147
+ }
148
+ return res.map(row => this.mapResult(row, meta));
149
+ }
150
+ mapResult(result, meta, populate = [], qb, map = {}) {
151
+ const ret = super.mapResult(result, meta);
152
+ /* istanbul ignore if */
153
+ if (!ret) {
154
+ return null;
155
+ }
156
+ if (qb) {
157
+ // here we map the aliased results (cartesian product) to an object graph
158
+ this.mapJoinedProps(ret, meta, populate, qb, ret, map);
159
+ }
160
+ return ret;
161
+ }
162
+ mapJoinedProps(result, meta, populate, qb, root, map, parentJoinPath) {
163
+ const joinedProps = this.joinedProps(meta, populate);
164
+ joinedProps.forEach(p => {
165
+ const [propName, ref] = p.field.split(':', 2);
166
+ const relation = meta.properties[propName];
167
+ /* istanbul ignore next */
168
+ if (!relation) {
169
+ return;
170
+ }
171
+ const pivotRefJoin = relation.kind === core_1.ReferenceKind.MANY_TO_MANY && ref;
172
+ const meta2 = this.metadata.find(relation.type);
173
+ let path = parentJoinPath ? `${parentJoinPath}.${relation.name}` : `${meta.name}.${relation.name}`;
174
+ if (!parentJoinPath) {
175
+ path = '[populate]' + path;
176
+ }
177
+ if (pivotRefJoin) {
178
+ path += '[pivot]';
179
+ }
180
+ const relationAlias = qb.getAliasForJoinPath(path, { matchPopulateJoins: true });
181
+ // pivot ref joins via joined strategy need to be handled separately here, as they dont join the target entity
182
+ if (pivotRefJoin) {
183
+ let item;
184
+ if (relation.inverseJoinColumns.length > 1) { // composite keys
185
+ item = relation.inverseJoinColumns.map(name => root[`${relationAlias}__${name}`]);
186
+ }
187
+ else {
188
+ const alias = `${relationAlias}__${relation.inverseJoinColumns[0]}`;
189
+ item = root[alias];
190
+ }
191
+ relation.joinColumns.forEach(name => delete root[`${relationAlias}__${name}`]);
192
+ relation.inverseJoinColumns.forEach(name => delete root[`${relationAlias}__${name}`]);
193
+ result[relation.name] ??= [];
194
+ if (item) {
195
+ result[relation.name].push(item);
196
+ }
197
+ return;
198
+ }
199
+ // If the primary key value for the relation is null, we know we haven't joined to anything
200
+ // and therefore we don't return any record (since all values would be null)
201
+ const hasPK = meta2.primaryKeys.every(pk => meta2.properties[pk].fieldNames.every(name => {
202
+ return root[`${relationAlias}__${name}`] != null;
203
+ }));
204
+ if (!hasPK) {
205
+ if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
206
+ result[relation.name] ??= [];
207
+ }
208
+ if ([core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(relation.kind)) {
209
+ result[relation.name] ??= null;
210
+ }
211
+ return;
212
+ }
213
+ const relationPojo = {};
214
+ meta2.props
215
+ .filter(prop => prop.persist === false && prop.fieldNames)
216
+ .forEach(prop => {
217
+ if (prop.fieldNames.length > 1) { // composite keys
218
+ relationPojo[prop.name] = prop.fieldNames.map(name => root[`${relationAlias}__${name}`]);
219
+ }
220
+ else {
221
+ const alias = `${relationAlias}__${prop.fieldNames[0]}`;
222
+ relationPojo[prop.name] = root[alias];
223
+ }
224
+ });
225
+ meta2.props
226
+ .filter(prop => this.platform.shouldHaveColumn(prop, p.children || []))
227
+ .forEach(prop => {
228
+ if (prop.fieldNames.length > 1) { // composite keys
229
+ relationPojo[prop.name] = prop.fieldNames.map(name => root[`${relationAlias}__${name}`]);
230
+ prop.fieldNames.map(name => delete root[`${relationAlias}__${name}`]);
231
+ }
232
+ else if (prop.runtimeType === 'Date') {
233
+ const alias = `${relationAlias}__${prop.fieldNames[0]}`;
234
+ relationPojo[prop.name] = (typeof root[alias] === 'string' ? new Date(root[alias]) : root[alias]);
235
+ delete root[alias];
236
+ }
237
+ else {
238
+ const alias = `${relationAlias}__${prop.fieldNames[0]}`;
239
+ relationPojo[prop.name] = root[alias];
240
+ delete root[alias];
241
+ }
242
+ });
243
+ if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
244
+ result[relation.name] ??= [];
245
+ result[relation.name].push(relationPojo);
246
+ }
247
+ else {
248
+ result[relation.name] = relationPojo;
249
+ }
250
+ const populateChildren = p.children || [];
251
+ this.mapJoinedProps(relationPojo, meta2, populateChildren, qb, root, map, path);
252
+ });
253
+ }
254
+ async count(entityName, where, options = {}) {
255
+ const meta = this.metadata.find(entityName);
256
+ if (meta?.virtual) {
257
+ return this.countVirtual(entityName, where, options);
258
+ }
259
+ const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false)
260
+ .indexHint(options.indexHint)
261
+ .comment(options.comments)
262
+ .hintComment(options.hintComments)
263
+ .groupBy(options.groupBy)
264
+ .having(options.having)
265
+ .populate(options.populate ?? [])
266
+ .withSchema(this.getSchemaName(meta, options))
267
+ .where(where);
268
+ return this.rethrow(qb.getCount());
269
+ }
270
+ async nativeInsert(entityName, data, options = {}) {
271
+ options.convertCustomTypes ??= true;
272
+ const meta = this.metadata.find(entityName);
273
+ const collections = this.extractManyToMany(entityName, data);
274
+ const pks = meta?.primaryKeys ?? [this.config.getNamingStrategy().referenceColumnName()];
275
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
276
+ const res = await this.rethrow(qb.insert(data).execute('run', false));
277
+ res.row = res.row || {};
278
+ let pk;
279
+ if (pks.length > 1) { // owner has composite pk
280
+ pk = core_1.Utils.getPrimaryKeyCond(data, pks);
281
+ }
282
+ else {
283
+ /* istanbul ignore next */
284
+ res.insertId = data[pks[0]] ?? res.insertId ?? res.row[pks[0]];
285
+ pk = [res.insertId];
286
+ }
287
+ await this.processManyToMany(meta, pk, collections, false, options);
288
+ return res;
289
+ }
290
+ async nativeInsertMany(entityName, data, options = {}) {
291
+ options.processCollections ??= true;
292
+ options.convertCustomTypes ??= true;
293
+ const meta = this.metadata.find(entityName)?.root;
294
+ const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
295
+ const pks = this.getPrimaryKeyFields(entityName);
296
+ const set = new Set();
297
+ data.forEach(row => core_1.Utils.keys(row).forEach(k => set.add(k)));
298
+ const props = [...set].map(name => meta?.properties[name] ?? { name, fieldNames: [name] });
299
+ let fields = core_1.Utils.flatten(props.map(prop => prop.fieldNames));
300
+ const duplicates = core_1.Utils.findDuplicates(fields);
301
+ const params = [];
302
+ if (duplicates.length) {
303
+ fields = core_1.Utils.unique(fields);
304
+ }
305
+ /* istanbul ignore next */
306
+ const tableName = meta ? this.getTableName(meta, options) : this.platform.quoteIdentifier(entityName);
307
+ let sql = `insert into ${tableName} `;
308
+ sql += fields.length > 0 ? '(' + fields.map(k => this.platform.quoteIdentifier(k)).join(', ') + ')' : `(${this.platform.quoteIdentifier(pks[0])})`;
309
+ if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
310
+ sql += ' values ';
311
+ }
312
+ else {
313
+ sql += ' ' + data.map(() => `select null as ${this.platform.quoteIdentifier(pks[0])}`).join(' union all ');
314
+ }
315
+ const addParams = (prop, row) => {
316
+ let value = row[prop.name];
317
+ if (prop.kind === core_1.ReferenceKind.EMBEDDED && prop.object) {
318
+ if (prop.array) {
319
+ for (let i = 0; i < value.length; i++) {
320
+ const item = value[i];
321
+ value[i] = this.mapDataToFieldNames(item, false, prop.embeddedProps, options.convertCustomTypes);
322
+ }
323
+ }
324
+ else {
325
+ value = this.mapDataToFieldNames(value, false, prop.embeddedProps, options.convertCustomTypes);
326
+ }
327
+ }
328
+ if (options.convertCustomTypes && prop.customType) {
329
+ params.push(prop.customType.convertToDatabaseValue(value, this.platform, { key: prop.name, mode: 'query-data' }));
330
+ return;
331
+ }
332
+ params.push(value);
333
+ };
334
+ if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
335
+ sql += data.map(row => {
336
+ const keys = [];
337
+ const usedDups = [];
338
+ props.forEach(prop => {
339
+ if (prop.fieldNames.length > 1) {
340
+ const param = core_1.Utils.flatten([...row[prop.name] ?? prop.fieldNames.map(() => null)]);
341
+ const key = param.map(() => '?');
342
+ prop.fieldNames.forEach((field, idx) => {
343
+ if (!duplicates.includes(field) || !usedDups.includes(field)) {
344
+ params.push(param[idx]);
345
+ keys.push(key[idx]);
346
+ usedDups.push(field);
347
+ }
348
+ });
349
+ }
350
+ else {
351
+ const field = prop.fieldNames[0];
352
+ if (!duplicates.includes(field) || !usedDups.includes(field)) {
353
+ if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(row[prop.name])) {
354
+ keys.push(prop.customType.convertToDatabaseValueSQL('?', this.platform));
355
+ }
356
+ else {
357
+ keys.push('?');
358
+ }
359
+ addParams(prop, row);
360
+ usedDups.push(field);
361
+ }
362
+ }
363
+ });
364
+ return '(' + (keys.join(', ') || 'default') + ')';
365
+ }).join(', ');
366
+ }
367
+ if (meta && this.platform.usesReturningStatement()) {
368
+ const returningProps = meta.props
369
+ .filter(prop => prop.persist !== false && prop.defaultRaw || prop.autoincrement || prop.generated)
370
+ .filter(prop => !(prop.name in data[0]) || core_1.Utils.isRawSql(data[0][prop.name]));
371
+ const returningFields = core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames));
372
+ /* istanbul ignore next */
373
+ sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
374
+ }
375
+ const res = await this.execute(sql, params, 'run', options.ctx);
376
+ let pk;
377
+ /* istanbul ignore next */
378
+ if (pks.length > 1) { // owner has composite pk
379
+ pk = data.map(d => core_1.Utils.getPrimaryKeyCond(d, pks));
380
+ }
381
+ else {
382
+ res.row ??= {};
383
+ res.rows ??= [];
384
+ pk = data.map((d, i) => d[pks[0]] ?? res.rows[i]?.[pks[0]]).map(d => [d]);
385
+ res.insertId = res.insertId || res.row[pks[0]];
386
+ }
387
+ for (let i = 0; i < collections.length; i++) {
388
+ await this.processManyToMany(meta, pk[i], collections[i], false, options);
389
+ }
390
+ return res;
391
+ }
392
+ async nativeUpdate(entityName, where, data, options = {}) {
393
+ options.convertCustomTypes ??= true;
394
+ const meta = this.metadata.find(entityName);
395
+ const pks = this.getPrimaryKeyFields(entityName);
396
+ const collections = this.extractManyToMany(entityName, data);
397
+ let res = { affectedRows: 0, insertId: 0, row: {} };
398
+ if (core_1.Utils.isPrimaryKey(where) && pks.length === 1) {
399
+ /* istanbul ignore next */
400
+ where = { [meta?.primaryKeys[0] ?? pks[0]]: where };
401
+ }
402
+ if (core_1.Utils.hasObjectKeys(data)) {
403
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes)
404
+ .withSchema(this.getSchemaName(meta, options));
405
+ if (options.upsert) {
406
+ /* istanbul ignore next */
407
+ const uniqueFields = options.onConflictFields ?? (core_1.Utils.isPlainObject(where) ? core_1.Utils.keys(where) : meta.primaryKeys);
408
+ const returning = (0, core_1.getOnConflictReturningFields)(meta, data, uniqueFields, options);
409
+ qb.insert(data)
410
+ .onConflict(uniqueFields)
411
+ .returning(returning);
412
+ if (!options.onConflictAction || options.onConflictAction === 'merge') {
413
+ const fields = (0, core_1.getOnConflictFields)(data, uniqueFields, options);
414
+ qb.merge(fields);
415
+ }
416
+ if (options.onConflictAction === 'ignore') {
417
+ qb.ignore();
418
+ }
419
+ }
420
+ else {
421
+ qb.update(data).where(where);
422
+ // reload generated columns and version fields
423
+ const returning = [];
424
+ meta?.props
425
+ .filter(prop => (prop.generated && !prop.primary) || prop.version)
426
+ .forEach(prop => returning.push(prop.name));
427
+ qb.returning(returning);
428
+ }
429
+ res = await this.rethrow(qb.execute('run', false));
430
+ }
431
+ /* istanbul ignore next */
432
+ const pk = pks.map(pk => core_1.Utils.extractPK(data[pk] || where, meta));
433
+ await this.processManyToMany(meta, pk, collections, true, options);
434
+ return res;
435
+ }
436
+ async nativeUpdateMany(entityName, where, data, options = {}) {
437
+ options.processCollections ??= true;
438
+ options.convertCustomTypes ??= true;
439
+ const meta = this.metadata.get(entityName);
440
+ if (options.upsert) {
441
+ const uniqueFields = options.onConflictFields ?? (core_1.Utils.isPlainObject(where[0]) ? Object.keys(where[0]).flatMap(key => core_1.Utils.splitPrimaryKeys(key)) : meta.primaryKeys);
442
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
443
+ const returning = (0, core_1.getOnConflictReturningFields)(meta, data[0], uniqueFields, options);
444
+ qb.insert(data)
445
+ .onConflict(uniqueFields)
446
+ .returning(returning);
447
+ if (!options.onConflictAction || options.onConflictAction === 'merge') {
448
+ const fields = (0, core_1.getOnConflictFields)(data[0], uniqueFields, options);
449
+ qb.merge(fields);
450
+ }
451
+ if (options.onConflictAction === 'ignore') {
452
+ qb.ignore();
453
+ }
454
+ return this.rethrow(qb.execute('run', false));
455
+ }
456
+ const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
457
+ const keys = new Set();
458
+ const returning = new Set();
459
+ data.forEach(row => {
460
+ core_1.Utils.keys(row).forEach(k => {
461
+ keys.add(k);
462
+ if (core_1.Utils.isRawSql(row[k])) {
463
+ returning.add(k);
464
+ }
465
+ });
466
+ });
467
+ // reload generated columns and version fields
468
+ meta?.props
469
+ .filter(prop => (prop.generated && !prop.primary) || prop.version)
470
+ .forEach(prop => returning.add(prop.name));
471
+ const pkCond = core_1.Utils.flatten(meta.primaryKeys.map(pk => meta.properties[pk].fieldNames)).map(pk => `${this.platform.quoteIdentifier(pk)} = ?`).join(' and ');
472
+ const params = [];
473
+ let sql = `update ${this.getTableName(meta, options)} set `;
474
+ const addParams = (prop, value) => {
475
+ if (prop.kind === core_1.ReferenceKind.EMBEDDED && prop.object) {
476
+ if (prop.array) {
477
+ for (let i = 0; i < value.length; i++) {
478
+ const item = value[i];
479
+ value[i] = this.mapDataToFieldNames(item, false, prop.embeddedProps, options.convertCustomTypes);
480
+ }
481
+ }
482
+ else {
483
+ value = this.mapDataToFieldNames(value, false, prop.embeddedProps, options.convertCustomTypes);
484
+ }
485
+ }
486
+ params.push(value);
487
+ };
488
+ keys.forEach(key => {
489
+ const prop = meta.properties[key];
490
+ prop.fieldNames.forEach((fieldName, fieldNameIdx) => {
491
+ sql += `${this.platform.quoteIdentifier(fieldName)} = case`;
492
+ where.forEach((cond, idx) => {
493
+ if (key in data[idx]) {
494
+ const pks = core_1.Utils.getOrderedPrimaryKeys(cond, meta);
495
+ sql += ` when (${pkCond}) then `;
496
+ if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(data[idx][key])) {
497
+ sql += prop.customType.convertToDatabaseValueSQL('?', this.platform);
498
+ }
499
+ else {
500
+ sql += '?';
501
+ }
502
+ params.push(...pks);
503
+ addParams(prop, prop.fieldNames.length > 1 ? data[idx][key]?.[fieldNameIdx] : data[idx][key]);
504
+ }
505
+ });
506
+ sql += ` else ${this.platform.quoteIdentifier(fieldName)} end, `;
507
+ return sql;
508
+ });
509
+ });
510
+ if (meta.versionProperty) {
511
+ const versionProperty = meta.properties[meta.versionProperty];
512
+ const quotedFieldName = this.platform.quoteIdentifier(versionProperty.fieldNames[0]);
513
+ sql += `${quotedFieldName} = `;
514
+ if (versionProperty.runtimeType === 'Date') {
515
+ sql += this.platform.getCurrentTimestampSQL(versionProperty.length);
516
+ }
517
+ else {
518
+ sql += `${quotedFieldName} + 1`;
519
+ }
520
+ sql += `, `;
521
+ }
522
+ sql = sql.substring(0, sql.length - 2) + ' where ';
523
+ const pkProps = meta.primaryKeys.concat(...meta.concurrencyCheckKeys);
524
+ const pks = core_1.Utils.flatten(pkProps.map(pk => meta.properties[pk].fieldNames));
525
+ sql += pks.length > 1 ? `(${pks.map(pk => `${this.platform.quoteIdentifier(pk)}`).join(', ')})` : this.platform.quoteIdentifier(pks[0]);
526
+ const conds = where.map(cond => {
527
+ if (core_1.Utils.isPlainObject(cond) && core_1.Utils.getObjectKeysSize(cond) === 1) {
528
+ cond = Object.values(cond)[0];
529
+ }
530
+ if (pks.length > 1) {
531
+ pkProps.forEach(pk => {
532
+ if (Array.isArray(cond[pk])) {
533
+ params.push(...core_1.Utils.flatten(cond[pk]));
534
+ }
535
+ else {
536
+ params.push(cond[pk]);
537
+ }
538
+ });
539
+ return `(${new Array(pks.length).fill('?').join(', ')})`;
540
+ }
541
+ params.push(cond);
542
+ return '?';
543
+ });
544
+ sql += ` in (${conds.join(', ')})`;
545
+ if (this.platform.usesReturningStatement() && returning.size > 0) {
546
+ const returningFields = core_1.Utils.flatten([...returning].map(prop => meta.properties[prop].fieldNames));
547
+ /* istanbul ignore next */
548
+ sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
549
+ }
550
+ const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx));
551
+ for (let i = 0; i < collections.length; i++) {
552
+ await this.processManyToMany(meta, where[i], collections[i], false, options);
553
+ }
554
+ return res;
555
+ }
556
+ async nativeDelete(entityName, where, options = {}) {
557
+ const meta = this.metadata.find(entityName);
558
+ const pks = this.getPrimaryKeyFields(entityName);
559
+ if (core_1.Utils.isPrimaryKey(where) && pks.length === 1) {
560
+ where = { [pks[0]]: where };
561
+ }
562
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false).delete(where).withSchema(this.getSchemaName(meta, options));
563
+ return this.rethrow(qb.execute('run', false));
564
+ }
565
+ async syncCollections(collections, options) {
566
+ const groups = {};
567
+ for (const coll of collections) {
568
+ const wrapped = (0, core_1.helper)(coll.owner);
569
+ const meta = wrapped.__meta;
570
+ const pks = wrapped.getPrimaryKeys(true);
571
+ const snap = coll.getSnapshot();
572
+ const includes = (arr, item) => !!arr.find(i => core_1.Utils.equals(i, item));
573
+ const snapshot = snap ? snap.map(item => (0, core_1.helper)(item).getPrimaryKeys(true)) : [];
574
+ const current = coll.getItems(false).map(item => (0, core_1.helper)(item).getPrimaryKeys(true));
575
+ const deleteDiff = snap ? snapshot.filter(item => !includes(current, item)) : true;
576
+ const insertDiff = current.filter(item => !includes(snapshot, item));
577
+ const target = snapshot.filter(item => includes(current, item)).concat(...insertDiff);
578
+ const equals = core_1.Utils.equals(current, target);
579
+ // wrong order if we just delete and insert to the end (only owning sides can have fixed order)
580
+ if (coll.property.owner && coll.property.fixedOrder && !equals && Array.isArray(deleteDiff)) {
581
+ deleteDiff.length = insertDiff.length = 0;
582
+ deleteDiff.push(...snapshot);
583
+ insertDiff.push(...current);
584
+ }
585
+ if (coll.property.kind === core_1.ReferenceKind.ONE_TO_MANY) {
586
+ const cols = coll.property.referencedColumnNames;
587
+ const qb = this.createQueryBuilder(coll.property.type, options?.ctx, 'write')
588
+ .withSchema(this.getSchemaName(meta, options));
589
+ if (coll.getSnapshot() === undefined) {
590
+ if (coll.property.orphanRemoval) {
591
+ const kqb = qb.delete({ [coll.property.mappedBy]: pks })
592
+ .getKnexQuery()
593
+ .whereNotIn(cols, insertDiff);
594
+ return this.rethrow(this.execute(kqb));
595
+ }
596
+ const kqb = qb.update({ [coll.property.mappedBy]: null })
597
+ .getKnexQuery()
598
+ .whereNotIn(cols, insertDiff);
599
+ return this.rethrow(this.execute(kqb));
600
+ }
601
+ const kqb = qb.update({ [coll.property.mappedBy]: pks })
602
+ .getKnexQuery()
603
+ .whereIn(cols, insertDiff);
604
+ return this.rethrow(this.execute(kqb));
605
+ }
606
+ /* istanbul ignore next */
607
+ const pivotMeta = this.metadata.find(coll.property.pivotEntity);
608
+ let schema = pivotMeta.schema;
609
+ if (schema === '*') {
610
+ const ownerSchema = wrapped.getSchema() === '*' ? this.config.get('schema') : wrapped.getSchema();
611
+ schema = coll.property.owner ? ownerSchema : this.config.get('schema');
612
+ }
613
+ else if (schema == null) {
614
+ schema = this.config.get('schema');
615
+ }
616
+ const tableName = `${schema ?? '_'}.${pivotMeta.tableName}`;
617
+ const persister = groups[tableName] ??= new PivotCollectionPersister_1.PivotCollectionPersister(pivotMeta, this, options?.ctx, schema);
618
+ persister.enqueueUpdate(coll.property, insertDiff, deleteDiff, pks);
619
+ }
620
+ for (const persister of core_1.Utils.values(groups)) {
621
+ await this.rethrow(persister.execute());
622
+ }
623
+ }
624
+ async loadFromPivotTable(prop, owners, where = {}, orderBy, ctx, options, pivotJoin) {
625
+ const pivotMeta = this.metadata.find(prop.pivotEntity);
626
+ const pivotProp1 = pivotMeta.relations[prop.owner ? 1 : 0];
627
+ const pivotProp2 = pivotMeta.relations[prop.owner ? 0 : 1];
628
+ const ownerMeta = this.metadata.find(pivotProp2.type);
629
+ options = { ...options };
630
+ const qb = this.createQueryBuilder(prop.pivotEntity, ctx, options.connectionType)
631
+ .withSchema(this.getSchemaName(pivotMeta, options))
632
+ .indexHint(options.indexHint)
633
+ .comment(options.comments)
634
+ .hintComment(options.hintComments);
635
+ const pivotAlias = qb.alias;
636
+ const pivotKey = pivotProp2.joinColumns.map(column => `${pivotAlias}.${column}`).join(core_1.Utils.PK_SEPARATOR);
637
+ const cond = {
638
+ [pivotKey]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) },
639
+ };
640
+ /* istanbul ignore if */
641
+ if (!core_1.Utils.isEmpty(where) && Object.keys(where).every(k => core_1.Utils.isOperator(k, false))) {
642
+ where = cond;
643
+ }
644
+ else {
645
+ where = { ...where, ...cond };
646
+ }
647
+ orderBy = this.getPivotOrderBy(prop, pivotProp1, pivotAlias, orderBy);
648
+ const populate = this.autoJoinOneToOneOwner(prop.targetMeta, []);
649
+ const fields = [];
650
+ const k1 = !prop.owner ? 'joinColumns' : 'inverseJoinColumns';
651
+ const k2 = prop.owner ? 'joinColumns' : 'inverseJoinColumns';
652
+ const cols = [
653
+ ...prop[k1].map(col => `${pivotAlias}.${col} as fk__${col}`),
654
+ ...prop[k2].map(col => `${pivotAlias}.${col} as fk__${col}`),
655
+ ];
656
+ fields.push(...cols);
657
+ if (!pivotJoin) {
658
+ const targetAlias = qb.getNextAlias(prop.targetMeta.tableName);
659
+ const targetSchema = this.getSchemaName(prop.targetMeta, options) ?? this.platform.getDefaultSchemaName();
660
+ qb.innerJoin(pivotProp1.name, targetAlias, {}, targetSchema);
661
+ const targetFields = this.buildFields(prop.targetMeta, (options.populate ?? []), [], qb, targetAlias, options.fields, options);
662
+ for (const field of targetFields) {
663
+ const f = field.toString();
664
+ fields.unshift(f.includes('.') ? field : `${targetAlias}.${f}`);
665
+ }
666
+ // we need to handle 1:1 owner auto-joins explicitly, as the QB type is the pivot table, not the target
667
+ populate.forEach(hint => {
668
+ const alias = qb.getNextAlias(prop.targetMeta.tableName);
669
+ qb.leftJoin(`${targetAlias}.${hint.field}`, alias);
670
+ // eslint-disable-next-line dot-notation
671
+ Object.values(qb['_joins']).forEach(join => {
672
+ const [propName] = hint.field.split(':', 2);
673
+ if (join.alias === alias && join.prop.name === propName) {
674
+ fields.push(...qb.helper.mapJoinColumns(qb.type, join));
675
+ }
676
+ });
677
+ });
678
+ }
679
+ qb.select(fields)
680
+ .where({ [pivotProp1.name]: where })
681
+ .orderBy(orderBy)
682
+ .setLockMode(options.lockMode, options.lockTableAliases);
683
+ if (owners.length === 1 && (options.offset != null || options.limit != null)) {
684
+ qb.limit(options.limit, options.offset);
685
+ }
686
+ const res = owners.length ? await this.rethrow(qb.execute('all', { mergeResults: false, mapResults: false })) : [];
687
+ const items = res.map((row) => super.mapResult(row, prop.targetMeta));
688
+ const map = {};
689
+ const pkProps = ownerMeta.getPrimaryProps();
690
+ owners.forEach(owner => {
691
+ const key = core_1.Utils.getPrimaryKeyHash(prop.joinColumns.map((_col, idx) => {
692
+ const pkProp = pkProps[idx];
693
+ return pkProp.customType ? pkProp.customType.convertToJSValue(owner[idx], this.platform) : owner[idx];
694
+ }));
695
+ return map[key] = [];
696
+ });
697
+ items.forEach((item) => {
698
+ const key = core_1.Utils.getPrimaryKeyHash(prop.joinColumns.map((col, idx) => {
699
+ const pkProp = pkProps[idx];
700
+ return pkProp.customType ? pkProp.customType.convertToJSValue(item[`fk__${col}`], this.platform) : item[`fk__${col}`];
701
+ }));
702
+ map[key].push(item);
703
+ prop.joinColumns.forEach(col => delete item[`fk__${col}`]);
704
+ prop.inverseJoinColumns.forEach((col, idx) => {
705
+ core_1.Utils.renameKey(item, `fk__${col}`, prop.targetMeta.primaryKeys[idx]);
706
+ });
707
+ });
708
+ return map;
709
+ }
710
+ getPivotOrderBy(prop, pivotProp, pivotAlias, orderBy) {
711
+ // FIXME this is ignoring the rest of the array items
712
+ if (!core_1.Utils.isEmpty(orderBy)) {
713
+ return [{ [pivotProp.name]: core_1.Utils.asArray(orderBy)[0] }];
714
+ }
715
+ if (!core_1.Utils.isEmpty(prop.orderBy)) {
716
+ return [{ [pivotProp.name]: core_1.Utils.asArray(prop.orderBy)[0] }];
717
+ }
718
+ if (prop.fixedOrder) {
719
+ return [{ [`${pivotAlias}.${prop.fixedOrderColumn}`]: core_1.QueryOrder.ASC }];
720
+ }
721
+ return [];
722
+ }
723
+ async execute(queryOrKnex, params = [], method = 'all', ctx) {
724
+ return this.rethrow(this.connection.execute(queryOrKnex, params, method, ctx));
725
+ }
726
+ /**
727
+ * 1:1 owner side needs to be marked for population so QB auto-joins the owner id
728
+ */
729
+ autoJoinOneToOneOwner(meta, populate, fields = []) {
730
+ if (!this.config.get('autoJoinOneToOneOwner')) {
731
+ return populate;
732
+ }
733
+ const relationsToPopulate = populate.map(({ field }) => field);
734
+ const toPopulate = meta.relations
735
+ .filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
736
+ .filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
737
+ .map(prop => ({ field: `${prop.name}:ref`, strategy: prop.strategy }));
738
+ return [...populate, ...toPopulate];
739
+ }
740
+ /**
741
+ * @internal
742
+ */
743
+ joinedProps(meta, populate, options) {
744
+ return populate.filter(hint => {
745
+ const [propName, ref] = hint.field.split(':', 2);
746
+ const prop = meta.properties[propName] || {};
747
+ return (options?.strategy || hint.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && ![core_1.ReferenceKind.SCALAR, core_1.ReferenceKind.EMBEDDED].includes(prop.kind);
748
+ });
749
+ }
750
+ /**
751
+ * @internal
752
+ */
753
+ mergeJoinedResult(rawResults, meta, joinedProps) {
754
+ const res = [];
755
+ const map = {};
756
+ for (const item of rawResults) {
757
+ const pk = core_1.Utils.getCompositeKeyHash(item, meta);
758
+ if (map[pk]) {
759
+ for (const hint of joinedProps) {
760
+ const [propName, ref] = hint.field.split(':', 2);
761
+ const prop = meta.properties[propName];
762
+ if (!item[propName]) {
763
+ continue;
764
+ }
765
+ if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && ref) {
766
+ map[pk][propName] = [...map[pk][propName], ...item[propName]];
767
+ continue;
768
+ }
769
+ switch (prop.kind) {
770
+ case core_1.ReferenceKind.ONE_TO_MANY:
771
+ case core_1.ReferenceKind.MANY_TO_MANY:
772
+ map[pk][propName] = this.mergeJoinedResult([...map[pk][propName], ...item[propName]], prop.targetMeta, hint.children ?? []);
773
+ break;
774
+ case core_1.ReferenceKind.MANY_TO_ONE:
775
+ case core_1.ReferenceKind.ONE_TO_ONE:
776
+ map[pk][propName] = this.mergeJoinedResult([map[pk][propName], item[propName]], prop.targetMeta, hint.children ?? [])[0];
777
+ break;
778
+ }
779
+ }
780
+ }
781
+ else {
782
+ map[pk] = item;
783
+ res.push(item);
784
+ }
785
+ }
786
+ return res;
787
+ }
788
+ getFieldsForJoinedLoad(qb, meta, explicitFields, populate = [], options, parentTableAlias, parentJoinPath) {
789
+ const fields = [];
790
+ const joinedProps = this.joinedProps(meta, populate, options);
791
+ if (explicitFields?.includes('*')) {
792
+ fields.push('*');
793
+ }
794
+ const shouldHaveColumn = (prop, populate, fields) => {
795
+ if (!this.platform.shouldHaveColumn(prop, populate)) {
796
+ return false;
797
+ }
798
+ if (!fields || prop.primary) {
799
+ return !fields?.includes('*');
800
+ }
801
+ return fields.some(f => f === prop.name || f.toString().startsWith(prop.name + '.'));
802
+ };
803
+ const populateWhereAll = options?._populateWhere === 'all' || core_1.Utils.isEmpty(options?._populateWhere);
804
+ // root entity is already handled, skip that
805
+ if (parentJoinPath) {
806
+ // alias all fields in the primary table
807
+ meta.props
808
+ .filter(prop => shouldHaveColumn(prop, populate, explicitFields))
809
+ .forEach(prop => fields.push(...this.mapPropToFieldNames(qb, prop, parentTableAlias)));
810
+ }
811
+ joinedProps.forEach(hint => {
812
+ const [propName, ref] = hint.field.split(':', 2);
813
+ const prop = meta.properties[propName];
814
+ const meta2 = this.metadata.find(prop.type);
815
+ const pivotRefJoin = prop.kind === core_1.ReferenceKind.MANY_TO_MANY && ref;
816
+ const tableAlias = qb.getNextAlias(prop.name);
817
+ const field = parentTableAlias ? `${parentTableAlias}.${prop.name}` : prop.name;
818
+ let path = parentJoinPath ? `${parentJoinPath}.${prop.name}` : `${meta.name}.${prop.name}`;
819
+ if (!parentJoinPath && populateWhereAll && !path.startsWith('[populate]')) {
820
+ path = '[populate]' + path;
821
+ }
822
+ const joinType = pivotRefJoin ? query_1.JoinType.pivotJoin : query_1.JoinType.leftJoin;
823
+ qb.join(field, tableAlias, {}, joinType, path);
824
+ if (pivotRefJoin) {
825
+ fields.push(...prop.joinColumns.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)), ...prop.inverseJoinColumns.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)));
826
+ }
827
+ const childExplicitFields = explicitFields?.filter(f => core_1.Utils.isPlainObject(f)).map(o => o[prop.name])[0] || [];
828
+ explicitFields?.forEach(f => {
829
+ if (typeof f === 'string' && f.startsWith(`${prop.name}.`)) {
830
+ childExplicitFields.push(f.substring(prop.name.length + 1));
831
+ }
832
+ });
833
+ if (!ref) {
834
+ fields.push(...this.getFieldsForJoinedLoad(qb, meta2, childExplicitFields.length === 0 ? undefined : childExplicitFields, hint.children, options, tableAlias, path));
835
+ }
836
+ });
837
+ return fields;
838
+ }
839
+ /**
840
+ * @internal
841
+ */
842
+ mapPropToFieldNames(qb, prop, tableAlias) {
843
+ const knex = this.connection.getKnex();
844
+ const aliased = knex.ref(tableAlias ? `${tableAlias}__${prop.fieldNames[0]}` : prop.fieldNames[0]).toString();
845
+ if (prop.customType?.convertToJSValueSQL && tableAlias) {
846
+ const prefixed = knex.ref(prop.fieldNames[0]).withSchema(tableAlias).toString();
847
+ return [(0, core_1.raw)(`${prop.customType.convertToJSValueSQL(prefixed, this.platform)} as ${aliased}`)];
848
+ }
849
+ if (prop.formula) {
850
+ const alias = knex.ref(tableAlias ?? qb.alias).toString();
851
+ return [(0, core_1.raw)(`${prop.formula(alias)} as ${aliased}`)];
852
+ }
853
+ if (tableAlias) {
854
+ return prop.fieldNames.map(fieldName => knex.ref(fieldName).withSchema(tableAlias).as(`${tableAlias}__${fieldName}`));
855
+ }
856
+ return prop.fieldNames;
857
+ }
858
+ /** @internal */
859
+ createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes, logging) {
860
+ const connectionType = this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
861
+ const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType, undefined, logging);
862
+ if (!convertCustomTypes) {
863
+ qb.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
864
+ }
865
+ return qb;
866
+ }
867
+ resolveConnectionType(args) {
868
+ if (args.ctx) {
869
+ return 'write';
870
+ }
871
+ else if (args.connectionType) {
872
+ return args.connectionType;
873
+ }
874
+ else if (this.config.get('preferReadReplicas') === true) {
875
+ return 'read';
876
+ }
877
+ return 'write';
878
+ }
879
+ extractManyToMany(entityName, data) {
880
+ if (!this.metadata.has(entityName)) {
881
+ return {};
882
+ }
883
+ const ret = {};
884
+ this.metadata.find(entityName).relations.forEach(prop => {
885
+ if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && data[prop.name]) {
886
+ ret[prop.name] = data[prop.name].map((item) => core_1.Utils.asArray(item));
887
+ delete data[prop.name];
888
+ }
889
+ });
890
+ return ret;
891
+ }
892
+ async processManyToMany(meta, pks, collections, clear, options) {
893
+ if (!meta) {
894
+ return;
895
+ }
896
+ for (const prop of meta.relations) {
897
+ if (collections[prop.name]) {
898
+ const pivotMeta = this.metadata.find(prop.pivotEntity);
899
+ const persister = new PivotCollectionPersister_1.PivotCollectionPersister(pivotMeta, this, options?.ctx, options?.schema);
900
+ persister.enqueueUpdate(prop, collections[prop.name], clear, pks);
901
+ await this.rethrow(persister.execute());
902
+ }
903
+ }
904
+ }
905
+ async lockPessimistic(entity, options) {
906
+ const meta = (0, core_1.helper)(entity).__meta;
907
+ const qb = this.createQueryBuilder(entity.constructor.name, options.ctx).withSchema(options.schema ?? meta.schema);
908
+ const cond = core_1.Utils.getPrimaryKeyCond(entity, meta.primaryKeys);
909
+ qb.select((0, core_1.raw)('1')).where(cond).setLockMode(options.lockMode, options.lockTableAliases);
910
+ await this.rethrow(qb.execute());
911
+ }
912
+ buildOrderBy(qb, meta, populate, options) {
913
+ const joinedProps = this.joinedProps(meta, populate, options);
914
+ // `options._populateWhere` is a copy of the value provided by user with a fallback to the global config option
915
+ // as `options.populateWhere` will be always recomputed to respect filters
916
+ const populateWhereAll = options._populateWhere !== 'infer' && !core_1.Utils.isEmpty(options._populateWhere);
917
+ const path = (populateWhereAll ? '[populate]' : '') + meta.className;
918
+ const populateOrderBy = this.buildPopulateOrderBy(qb, meta, core_1.Utils.asArray(options.populateOrderBy ?? options.orderBy), path);
919
+ const joinedPropsOrderBy = this.buildJoinedPropsOrderBy(qb, meta, joinedProps, options, path);
920
+ return [...core_1.Utils.asArray(options.orderBy), ...populateOrderBy, ...joinedPropsOrderBy];
921
+ }
922
+ buildPopulateOrderBy(qb, meta, populateOrderBy, parentPath, parentAlias = qb.alias) {
923
+ const orderBy = [];
924
+ for (let i = 0; i < populateOrderBy.length; i++) {
925
+ const orderHint = populateOrderBy[i];
926
+ for (const propName of core_1.Utils.keys(orderHint)) {
927
+ const prop = meta.properties[propName];
928
+ const meta2 = this.metadata.find(prop.type);
929
+ const childOrder = orderHint[prop.name];
930
+ let path = `${parentPath}.${propName}`;
931
+ if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && typeof childOrder !== 'object') {
932
+ path += '[pivot]';
933
+ }
934
+ const join = qb.getJoinForPath(path, { matchPopulateJoins: true });
935
+ const propAlias = qb.getAliasForJoinPath(join ?? path, { matchPopulateJoins: true }) ?? parentAlias;
936
+ if (!join && parentAlias === qb.alias) {
937
+ continue;
938
+ }
939
+ if (![core_1.ReferenceKind.SCALAR, core_1.ReferenceKind.EMBEDDED].includes(prop.kind) && typeof childOrder === 'object') {
940
+ const children = this.buildPopulateOrderBy(qb, meta2, core_1.Utils.asArray(childOrder), path, propAlias);
941
+ orderBy.push(...children);
942
+ continue;
943
+ }
944
+ if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && join) {
945
+ if (prop.fixedOrderColumn) {
946
+ orderBy.push({ [`${join.alias}.${prop.fixedOrderColumn}`]: childOrder });
947
+ }
948
+ else {
949
+ for (const col of prop.inverseJoinColumns) {
950
+ orderBy.push({ [`${join.ownerAlias}.${col}`]: childOrder });
951
+ }
952
+ }
953
+ continue;
954
+ }
955
+ const order = typeof childOrder === 'object' ? childOrder[propName] : childOrder;
956
+ orderBy.push({ [`${propAlias}.${propName}`]: order });
957
+ }
958
+ }
959
+ return orderBy;
960
+ }
961
+ buildJoinedPropsOrderBy(qb, meta, populate, options, parentPath) {
962
+ const orderBy = [];
963
+ const joinedProps = this.joinedProps(meta, populate, options);
964
+ for (const hint of joinedProps) {
965
+ const [propName, ref] = hint.field.split(':', 2);
966
+ const prop = meta.properties[propName];
967
+ const propOrderBy = prop.orderBy;
968
+ let path = `${parentPath}.${propName}`;
969
+ if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && ref) {
970
+ path += '[pivot]';
971
+ }
972
+ const join = qb.getJoinForPath(path, { matchPopulateJoins: true });
973
+ const propAlias = qb.getAliasForJoinPath(join ?? path, { matchPopulateJoins: true });
974
+ const meta2 = this.metadata.find(prop.type);
975
+ if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && prop.fixedOrder && join) {
976
+ orderBy.push({ [`${(join.ownerAlias)}.${prop.fixedOrderColumn}`]: core_1.QueryOrder.ASC });
977
+ }
978
+ if (propOrderBy) {
979
+ core_1.Utils.keys(propOrderBy).forEach(field => {
980
+ orderBy.push({ [`${propAlias}.${field}`]: propOrderBy[field] });
981
+ });
982
+ }
983
+ if (hint.children) {
984
+ const buildJoinedPropsOrderBy = this.buildJoinedPropsOrderBy(qb, meta2, hint.children, options, path);
985
+ orderBy.push(...buildJoinedPropsOrderBy);
986
+ }
987
+ }
988
+ return orderBy;
989
+ }
990
+ normalizeFields(fields, prefix = '') {
991
+ const ret = [];
992
+ for (const field of fields) {
993
+ if (typeof field === 'string') {
994
+ ret.push(prefix + field);
995
+ continue;
996
+ }
997
+ if (core_1.Utils.isPlainObject(field)) {
998
+ for (const key of Object.keys(field)) {
999
+ ret.push(...this.normalizeFields(field[key], key + '.'));
1000
+ }
1001
+ }
1002
+ }
1003
+ return ret;
1004
+ }
1005
+ processField(meta, prop, field, ret, populate, joinedProps, qb) {
1006
+ if (!prop || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner)) {
1007
+ return;
1008
+ }
1009
+ if (prop.kind === core_1.ReferenceKind.EMBEDDED) {
1010
+ if (prop.object) {
1011
+ ret.push(prop.name);
1012
+ return;
1013
+ }
1014
+ const parts = field.split('.');
1015
+ const top = parts.shift();
1016
+ for (const key of Object.keys(prop.embeddedProps)) {
1017
+ if (!top || key === top) {
1018
+ this.processField(meta, prop.embeddedProps[key], parts.join('.'), ret, populate, joinedProps, qb);
1019
+ }
1020
+ }
1021
+ return;
1022
+ }
1023
+ ret.push(prop.name);
1024
+ }
1025
+ buildFields(meta, populate, joinedProps, qb, alias, fields, options) {
1026
+ const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => p.field === prop.name || p.all));
1027
+ const hasLazyFormulas = meta.props.some(p => p.lazy && p.formula);
1028
+ const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL && p.persist !== false);
1029
+ const hasExplicitFields = !!fields;
1030
+ const ret = [];
1031
+ let addFormulas = false;
1032
+ // handle root entity properties first, this is used for both strategies in the same way
1033
+ if (fields) {
1034
+ for (const field of this.normalizeFields(fields)) {
1035
+ if (field === '*') {
1036
+ ret.push('*');
1037
+ continue;
1038
+ }
1039
+ const parts = field.split('.');
1040
+ const rootPropName = parts.shift(); // first one is the `prop`
1041
+ const prop = core_1.QueryHelper.findProperty(rootPropName, {
1042
+ metadata: this.metadata,
1043
+ platform: this.platform,
1044
+ entityName: meta.className,
1045
+ where: {},
1046
+ aliasMap: qb.getAliasMap(),
1047
+ });
1048
+ this.processField(meta, prop, parts.join('.'), ret, populate, joinedProps, qb);
1049
+ }
1050
+ if (!fields.includes('*') && !fields.includes(`${qb.alias}.*`)) {
1051
+ ret.unshift(...meta.primaryKeys.filter(pk => !fields.includes(pk)));
1052
+ }
1053
+ }
1054
+ else if (lazyProps.filter(p => !p.formula).length > 0) {
1055
+ const props = meta.props.filter(prop => this.platform.shouldHaveColumn(prop, populate, false));
1056
+ ret.push(...core_1.Utils.flatten(props.filter(p => !lazyProps.includes(p)).map(p => p.fieldNames)));
1057
+ addFormulas = true;
1058
+ }
1059
+ else if (hasLazyFormulas || requiresSQLConversion) {
1060
+ ret.push('*');
1061
+ addFormulas = true;
1062
+ }
1063
+ else {
1064
+ ret.push('*');
1065
+ }
1066
+ if (ret.length > 0 && !hasExplicitFields && addFormulas) {
1067
+ meta.props
1068
+ .filter(prop => prop.formula && !lazyProps.includes(prop))
1069
+ .forEach(prop => {
1070
+ const a = this.connection.getKnex().ref(alias).toString();
1071
+ const aliased = this.connection.getKnex().ref(prop.fieldNames[0]).toString();
1072
+ ret.push((0, core_1.raw)(`${prop.formula(a)} as ${aliased}`));
1073
+ });
1074
+ meta.props
1075
+ .filter(prop => prop.hasConvertToDatabaseValueSQL || prop.hasConvertToJSValueSQL)
1076
+ .forEach(prop => ret.push(prop.name));
1077
+ }
1078
+ // add joined relations after the root entity fields
1079
+ if (joinedProps.length > 0) {
1080
+ ret.push(...this.getFieldsForJoinedLoad(qb, meta, fields, populate, options, alias));
1081
+ }
1082
+ return core_1.Utils.unique(ret);
1083
+ }
1084
+ }
1085
+ exports.AbstractSqlDriver = AbstractSqlDriver;