@yandjin-mikro-orm/knex 6.1.4-rc-sti-changes-1

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