@mikro-orm/knex 6.0.0-dev.10 → 6.0.0-dev.101

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 (39) hide show
  1. package/AbstractSqlConnection.d.ts +8 -4
  2. package/AbstractSqlConnection.js +30 -18
  3. package/AbstractSqlDriver.d.ts +22 -22
  4. package/AbstractSqlDriver.js +233 -88
  5. package/AbstractSqlPlatform.d.ts +5 -4
  6. package/AbstractSqlPlatform.js +34 -5
  7. package/MonkeyPatchable.d.ts +1 -0
  8. package/MonkeyPatchable.js +3 -0
  9. package/README.md +64 -107
  10. package/SqlEntityManager.d.ts +2 -6
  11. package/SqlEntityManager.js +5 -9
  12. package/SqlEntityRepository.d.ts +7 -6
  13. package/SqlEntityRepository.js +11 -8
  14. package/index.mjs +22 -6
  15. package/package.json +8 -8
  16. package/query/ArrayCriteriaNode.d.ts +3 -3
  17. package/query/CriteriaNode.d.ts +8 -8
  18. package/query/CriteriaNode.js +16 -9
  19. package/query/CriteriaNodeFactory.d.ts +6 -6
  20. package/query/CriteriaNodeFactory.js +10 -13
  21. package/query/ObjectCriteriaNode.d.ts +3 -3
  22. package/query/ObjectCriteriaNode.js +12 -8
  23. package/query/QueryBuilder.d.ts +41 -19
  24. package/query/QueryBuilder.js +319 -110
  25. package/query/QueryBuilderHelper.d.ts +9 -13
  26. package/query/QueryBuilderHelper.js +123 -97
  27. package/query/ScalarCriteriaNode.d.ts +3 -2
  28. package/query/ScalarCriteriaNode.js +9 -6
  29. package/query/enums.js +1 -1
  30. package/schema/DatabaseSchema.d.ts +5 -2
  31. package/schema/DatabaseSchema.js +26 -8
  32. package/schema/DatabaseTable.d.ts +4 -2
  33. package/schema/DatabaseTable.js +53 -28
  34. package/schema/SchemaComparator.js +12 -2
  35. package/schema/SchemaHelper.d.ts +6 -4
  36. package/schema/SchemaHelper.js +11 -4
  37. package/schema/SqlSchemaGenerator.d.ts +3 -6
  38. package/schema/SqlSchemaGenerator.js +61 -28
  39. package/typings.d.ts +12 -7
@@ -1,6 +1,5 @@
1
1
  import type { Knex } from 'knex';
2
- import type { Dictionary, EntityData, EntityMetadata, EntityProperty, FlatQueryOrderMap, QBFilterQuery } from '@mikro-orm/core';
3
- import { LockMode } from '@mikro-orm/core';
2
+ import { LockMode, type Dictionary, type EntityData, type EntityKey, type EntityMetadata, type EntityProperty, type FlatQueryOrderMap, type QBFilterQuery } from '@mikro-orm/core';
4
3
  import { QueryType } from './enums';
5
4
  import type { Field, JoinOptions } from '../typings';
6
5
  import type { AbstractSqlDriver } from '../AbstractSqlDriver';
@@ -16,14 +15,13 @@ export declare class QueryBuilderHelper {
16
15
  private readonly driver;
17
16
  private readonly platform;
18
17
  private readonly metadata;
19
- constructor(entityName: string, alias: string, aliasMap: Dictionary<Alias>, subQueries: Dictionary<string>, knex: Knex, driver: AbstractSqlDriver);
18
+ constructor(entityName: string, alias: string, aliasMap: Dictionary<Alias<any>>, subQueries: Dictionary<string>, knex: Knex, driver: AbstractSqlDriver);
20
19
  mapper(field: string | Knex.Raw, type?: QueryType): string;
21
20
  mapper(field: string | Knex.Raw, type?: QueryType, value?: any, alias?: string | null): string;
22
21
  processData(data: Dictionary, convertCustomTypes: boolean, multi?: boolean): any;
23
- joinOneToReference(prop: EntityProperty, ownerAlias: string, alias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond?: Dictionary): JoinOptions;
24
- joinManyToOneReference(prop: EntityProperty, ownerAlias: string, alias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond?: Dictionary): JoinOptions;
25
- joinManyToManyReference(prop: EntityProperty, ownerAlias: string, alias: string, pivotAlias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond: Dictionary, path: string): Dictionary<JoinOptions>;
26
- joinPivotTable(field: string, prop: EntityProperty, ownerAlias: string, alias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond?: Dictionary): JoinOptions;
22
+ joinOneToReference(prop: EntityProperty, ownerAlias: string, alias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond?: Dictionary, schema?: string): JoinOptions;
23
+ joinManyToOneReference(prop: EntityProperty, ownerAlias: string, alias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond?: Dictionary, schema?: string): JoinOptions;
24
+ joinManyToManyReference(prop: EntityProperty, ownerAlias: string, alias: string, pivotAlias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond: Dictionary, path: string, schema?: string): Dictionary<JoinOptions>;
27
25
  processJoins(qb: Knex.QueryBuilder, joins: Dictionary<JoinOptions>, schema?: string): void;
28
26
  private processJoinClause;
29
27
  private wrapQueryGroup;
@@ -43,16 +41,14 @@ export declare class QueryBuilderHelper {
43
41
  }[], qb: Knex.QueryBuilder): void;
44
42
  appendQueryCondition(type: QueryType, cond: any, qb: Knex.QueryBuilder, operator?: '$and' | '$or', method?: 'where' | 'having'): void;
45
43
  private appendQuerySubCondition;
46
- private processCustomExpression;
47
44
  private processObjectSubCondition;
48
45
  private getOperatorReplacement;
49
46
  getQueryOrder(type: QueryType, orderBy: FlatQueryOrderMap | FlatQueryOrderMap[], populate: Dictionary<string>): string;
50
47
  getQueryOrderFromObject(type: QueryType, orderBy: FlatQueryOrderMap, populate: Dictionary<string>): string;
51
- finalize(type: QueryType, qb: Knex.QueryBuilder, meta?: EntityMetadata): void;
52
- splitField(field: string, greedyAlias?: boolean): [string, string];
48
+ finalize(type: QueryType, qb: Knex.QueryBuilder, meta?: EntityMetadata, data?: Dictionary, returning?: Field<any>[]): void;
49
+ splitField<T>(field: EntityKey<T>, greedyAlias?: boolean): [string, EntityKey<T>];
53
50
  getLockSQL(qb: Knex.QueryBuilder, lockMode: LockMode, lockTables?: string[]): void;
54
51
  updateVersionProperty(qb: Knex.QueryBuilder, data: Dictionary): void;
55
- static isCustomExpression(field: string, hasAlias?: boolean): boolean;
56
52
  private prefix;
57
53
  private appendGroupCondition;
58
54
  private isPrefixed;
@@ -61,9 +57,9 @@ export declare class QueryBuilderHelper {
61
57
  isTableNameAliasRequired(type?: QueryType): boolean;
62
58
  private mapData;
63
59
  }
64
- export interface Alias {
60
+ export interface Alias<T> {
65
61
  aliasName: string;
66
62
  entityName: string;
67
- metadata?: EntityMetadata;
63
+ metadata?: EntityMetadata<T>;
68
64
  subQuery?: Knex.QueryBuilder;
69
65
  }
@@ -8,6 +8,14 @@ const enums_1 = require("./enums");
8
8
  * @internal
9
9
  */
10
10
  class QueryBuilderHelper {
11
+ entityName;
12
+ alias;
13
+ aliasMap;
14
+ subQueries;
15
+ knex;
16
+ driver;
17
+ platform;
18
+ metadata;
11
19
  constructor(entityName, alias, aliasMap, subQueries, knex, driver) {
12
20
  this.entityName = entityName;
13
21
  this.alias = alias;
@@ -19,6 +27,10 @@ class QueryBuilderHelper {
19
27
  this.metadata = this.driver.getMetadata();
20
28
  }
21
29
  mapper(field, type = enums_1.QueryType.SELECT, value, alias) {
30
+ if (core_1.Utils.isRawSql(field)) {
31
+ return this.knex.raw(field.sql, field.params);
32
+ }
33
+ /* istanbul ignore next */
22
34
  if (typeof field !== 'string') {
23
35
  return field;
24
36
  }
@@ -36,12 +48,26 @@ class QueryBuilderHelper {
36
48
  parts.push(this.mapper(a !== this.alias ? `${a}.${f}` : f, type, value, alias));
37
49
  }
38
50
  }
51
+ // flatten the value if we see we are expanding nested composite key
52
+ // hackish, but cleaner solution would require quite a lot of refactoring
53
+ if (fields.length !== parts.length && Array.isArray(value)) {
54
+ value.forEach(row => {
55
+ if (Array.isArray(row)) {
56
+ const tmp = core_1.Utils.flatten(row);
57
+ row.length = 0;
58
+ row.push(...tmp);
59
+ }
60
+ });
61
+ }
39
62
  return this.knex.raw('(' + parts.map(part => this.knex.ref(part)).join(', ') + ')');
40
63
  }
41
- let ret = field;
42
- const customExpression = QueryBuilderHelper.isCustomExpression(field, !!alias);
64
+ const rawField = core_1.RawQueryFragment.getKnownFragment(field);
65
+ if (rawField) {
66
+ return this.knex.raw(rawField.sql, rawField.params);
67
+ }
43
68
  const [a, f] = this.splitField(field);
44
69
  const prop = this.getProperty(f, a);
70
+ let ret = field;
45
71
  // embeddable nested path instead of a regular property with table alias, reset alias
46
72
  if (prop?.name === a && prop.embeddedProps[f]) {
47
73
  return this.alias + '.' + prop.fieldNames[0];
@@ -56,7 +82,7 @@ class QueryBuilderHelper {
56
82
  const as = alias === null ? '' : ` as ${aliased}`;
57
83
  return this.knex.raw(`${prop.formula(alias2)}${as}`);
58
84
  }
59
- if (prop?.customType?.convertToJSValueSQL) {
85
+ if (prop?.hasConvertToJSValueSQL) {
60
86
  const prefixed = this.prefix(field, isTableNameAliasRequired, true);
61
87
  const valueSQL = prop.customType.convertToJSValueSQL(prefixed, this.platform);
62
88
  if (alias === null) {
@@ -65,15 +91,12 @@ class QueryBuilderHelper {
65
91
  return this.knex.raw(`${valueSQL} as ${this.platform.quoteIdentifier(alias ?? prop.fieldNames[0])}`);
66
92
  }
67
93
  // do not wrap custom expressions
68
- if (!customExpression) {
94
+ if (!rawField) {
69
95
  ret = this.prefix(field);
70
96
  }
71
97
  if (alias) {
72
98
  ret += ' as ' + alias;
73
99
  }
74
- if (customExpression) {
75
- return this.knex.raw(ret, value);
76
- }
77
100
  if (!isTableNameAliasRequired || this.isPrefixed(ret) || noPrefix) {
78
101
  return ret;
79
102
  }
@@ -91,39 +114,40 @@ class QueryBuilderHelper {
91
114
  }
92
115
  return data;
93
116
  }
94
- joinOneToReference(prop, ownerAlias, alias, type, cond = {}) {
117
+ joinOneToReference(prop, ownerAlias, alias, type, cond = {}, schema) {
95
118
  const prop2 = prop.targetMeta.properties[prop.mappedBy || prop.inversedBy];
96
119
  const table = this.getTableName(prop.type);
97
- const schema = this.driver.getSchemaName(prop.targetMeta);
98
120
  const joinColumns = prop.owner ? prop.referencedColumnNames : prop2.joinColumns;
99
121
  const inverseJoinColumns = prop.referencedColumnNames;
100
122
  const primaryKeys = prop.owner ? prop.joinColumns : prop2.referencedColumnNames;
123
+ schema ??= prop.targetMeta?.schema === '*' ? '*' : this.driver.getSchemaName(prop.targetMeta);
101
124
  return {
102
125
  prop, type, cond, ownerAlias, alias, table, schema,
103
126
  joinColumns, inverseJoinColumns, primaryKeys,
104
127
  };
105
128
  }
106
- joinManyToOneReference(prop, ownerAlias, alias, type, cond = {}) {
129
+ joinManyToOneReference(prop, ownerAlias, alias, type, cond = {}, schema) {
107
130
  return {
108
131
  prop, type, cond, ownerAlias, alias,
109
132
  table: this.getTableName(prop.type),
110
- schema: this.driver.getSchemaName(prop.targetMeta),
133
+ schema: this.driver.getSchemaName(prop.targetMeta, { schema }),
111
134
  joinColumns: prop.referencedColumnNames,
112
135
  primaryKeys: prop.fieldNames,
113
136
  };
114
137
  }
115
- joinManyToManyReference(prop, ownerAlias, alias, pivotAlias, type, cond, path) {
138
+ joinManyToManyReference(prop, ownerAlias, alias, pivotAlias, type, cond, path, schema) {
116
139
  const pivotMeta = this.metadata.find(prop.pivotEntity);
117
140
  const ret = {
118
141
  [`${ownerAlias}.${prop.name}#${pivotAlias}`]: {
119
- prop, type, cond, ownerAlias,
142
+ prop, type, ownerAlias,
120
143
  alias: pivotAlias,
121
144
  inverseAlias: alias,
122
145
  joinColumns: prop.joinColumns,
123
146
  inverseJoinColumns: prop.inverseJoinColumns,
124
147
  primaryKeys: prop.referencedColumnNames,
148
+ cond: {},
125
149
  table: pivotMeta.tableName,
126
- schema: this.driver.getSchemaName(pivotMeta),
150
+ schema: this.driver.getSchemaName(pivotMeta, { schema }),
127
151
  path: path.endsWith('[pivot]') ? path : `${path}[pivot]`,
128
152
  },
129
153
  };
@@ -131,22 +155,10 @@ class QueryBuilderHelper {
131
155
  return ret;
132
156
  }
133
157
  const prop2 = prop.owner ? pivotMeta.relations[1] : pivotMeta.relations[0];
134
- ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type);
158
+ ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type, cond, schema);
135
159
  ret[`${pivotAlias}.${prop2.name}#${alias}`].path = path;
136
160
  return ret;
137
161
  }
138
- joinPivotTable(field, prop, ownerAlias, alias, type, cond = {}) {
139
- const pivotMeta = this.metadata.find(field);
140
- const prop2 = pivotMeta.relations[0] === prop ? pivotMeta.relations[1] : pivotMeta.relations[0];
141
- return {
142
- prop, type, cond, ownerAlias, alias,
143
- table: pivotMeta.collection,
144
- schema: pivotMeta.schema,
145
- joinColumns: prop.joinColumns,
146
- inverseJoinColumns: prop2.joinColumns,
147
- primaryKeys: prop.referencedColumnNames,
148
- };
149
- }
150
162
  processJoins(qb, joins, schema) {
151
163
  Object.values(joins).forEach(join => {
152
164
  let table = `${join.table} as ${join.alias}`;
@@ -162,6 +174,11 @@ class QueryBuilderHelper {
162
174
  const right = `${join.alias}.${join.joinColumns[idx]}`;
163
175
  conditions.push(`${this.knex.ref(left)} = ${this.knex.ref(right)}`);
164
176
  });
177
+ if (join.prop.targetMeta.discriminatorValue && !join.path?.endsWith('[pivot]')) {
178
+ const typeProperty = join.prop.targetMeta.root.discriminatorColumn;
179
+ const alias = join.inverseAlias ?? join.alias;
180
+ join.cond[`${alias}.${typeProperty}`] = join.prop.targetMeta.discriminatorValue;
181
+ }
165
182
  Object.keys(join.cond).forEach(key => {
166
183
  conditions.push(this.processJoinClause(key, join.cond[key], params));
167
184
  });
@@ -212,20 +229,14 @@ class QueryBuilderHelper {
212
229
  if (operator === '$exists') {
213
230
  value = null;
214
231
  }
215
- if (QueryBuilderHelper.isCustomExpression(key)) {
216
- // unwind parameters when ? found in field name
217
- const count = key.concat('?').match(/\?/g).length - 1;
218
- const values = core_1.Utils.asArray(value);
219
- const params1 = values.slice(0, count).map((c) => core_1.Utils.isObject(c) ? JSON.stringify(c) : c);
220
- const params2 = values.slice(count);
221
- const k = this.mapper(key, enums_1.QueryType.SELECT, params1);
222
- const { sql, bindings } = k.toSQL().toNative();
223
- if (params2.length > 0) {
224
- params.push(...bindings);
225
- params.push(...params2);
226
- return sql + ' = ?';
232
+ const rawField = core_1.RawQueryFragment.getKnownFragment(key);
233
+ if (rawField) {
234
+ let sql = rawField.sql;
235
+ params.push(...rawField.params);
236
+ params.push(...core_1.Utils.asArray(value));
237
+ if (core_1.Utils.asArray(value).length > 0) {
238
+ sql += ' = ?';
227
239
  }
228
- params.push(...bindings);
229
240
  return sql;
230
241
  }
231
242
  const sql = this.mapper(key);
@@ -241,7 +252,7 @@ class QueryBuilderHelper {
241
252
  return `(${parts.join(` ${core_1.GroupOperator[operator]} `)})`;
242
253
  }
243
254
  mapJoinColumns(type, join) {
244
- if (join.prop && join.prop.reference === core_1.ReferenceType.ONE_TO_ONE && !join.prop.owner) {
255
+ if (join.prop && join.prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !join.prop.owner) {
245
256
  return join.prop.fieldNames.map((fieldName, idx) => {
246
257
  return this.mapper(`${join.alias}.${join.inverseJoinColumns[idx]}`, type, undefined, fieldName);
247
258
  });
@@ -254,7 +265,7 @@ class QueryBuilderHelper {
254
265
  isOneToOneInverse(field) {
255
266
  const meta = this.metadata.find(this.entityName);
256
267
  const prop = meta.properties[field];
257
- return prop && prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner;
268
+ return prop && prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner;
258
269
  }
259
270
  getTableName(entityName) {
260
271
  const meta = this.metadata.find(entityName);
@@ -293,13 +304,13 @@ class QueryBuilderHelper {
293
304
  }
294
305
  appendOnConflictClause(type, onConflict, qb) {
295
306
  onConflict.forEach(item => {
296
- const sub = qb.onConflict(item.fields);
307
+ const sub = item.fields.length > 0 ? qb.onConflict(item.fields) : qb.onConflict();
297
308
  core_1.Utils.runIfNotEmpty(() => sub.ignore(), item.ignore);
298
309
  core_1.Utils.runIfNotEmpty(() => {
299
310
  let mergeParam = item.merge;
300
311
  if (core_1.Utils.isObject(item.merge)) {
301
312
  mergeParam = {};
302
- Object.keys(item.merge).forEach(key => {
313
+ core_1.Utils.keys(item.merge).forEach(key => {
303
314
  const k = this.mapper(key, type);
304
315
  mergeParam[k] = item.merge[key];
305
316
  });
@@ -330,34 +341,29 @@ class QueryBuilderHelper {
330
341
  }
331
342
  appendQuerySubCondition(qb, type, method, cond, key, operator) {
332
343
  const m = operator === '$or' ? 'orWhere' : method;
344
+ if (cond[key] instanceof core_1.RawQueryFragment) {
345
+ cond[key] = this.knex.raw(cond[key].sql, cond[key].params);
346
+ }
333
347
  if (this.isSimpleRegExp(cond[key])) {
334
348
  return void qb[m](this.mapper(key, type), 'like', this.getRegExpParam(cond[key]));
335
349
  }
336
350
  if (core_1.Utils.isPlainObject(cond[key]) || cond[key] instanceof RegExp) {
337
351
  return this.processObjectSubCondition(cond, key, qb, method, m, type);
338
352
  }
339
- if (QueryBuilderHelper.isCustomExpression(key)) {
340
- return this.processCustomExpression(qb, m, key, cond, type);
341
- }
342
353
  const op = cond[key] === null ? 'is' : '=';
354
+ const raw = core_1.RawQueryFragment.getKnownFragment(key);
355
+ if (raw) {
356
+ const value = core_1.Utils.asArray(cond[key]);
357
+ if (value.length > 0) {
358
+ return void qb[m](this.knex.raw(raw.sql, raw.params), op, value[0]);
359
+ }
360
+ return void qb[m](this.knex.raw(raw.sql, raw.params));
361
+ }
343
362
  if (this.subQueries[key]) {
344
363
  return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), op, cond[key]);
345
364
  }
346
365
  qb[m](this.mapper(key, type, cond[key], null), op, cond[key]);
347
366
  }
348
- processCustomExpression(clause, m, key, cond, type) {
349
- // unwind parameters when ? found in field name
350
- const count = key.concat('?').match(/\?/g).length - 1;
351
- const value = core_1.Utils.asArray(cond[key]);
352
- const params1 = value.slice(0, count).map((c) => core_1.Utils.isObject(c) ? JSON.stringify(c) : c);
353
- const params2 = value.slice(count);
354
- const k = this.mapper(key, type, params1);
355
- if (params2.length > 0) {
356
- const val = params2.length === 1 && params2[0] === null ? null : this.knex.raw('?', params2);
357
- return void clause[m](k, val);
358
- }
359
- clause[m](k);
360
- }
361
367
  processObjectSubCondition(cond, key, qb, method, m, type) {
362
368
  // grouped condition for one field
363
369
  let value = cond[key];
@@ -376,7 +382,8 @@ class QueryBuilderHelper {
376
382
  const replacement = this.getOperatorReplacement(op, value);
377
383
  const fields = core_1.Utils.splitPrimaryKeys(key);
378
384
  if (fields.length > 1 && Array.isArray(value[op]) && !value[op].every((v) => Array.isArray(v))) {
379
- value[op] = this.knex.raw(`(${fields.map(() => '?').join(', ')})`, value[op]);
385
+ const tmp = value[op].length === 1 && core_1.Utils.isPlainObject(value[op][0]) ? fields.map(f => value[op][0][f]) : value[op];
386
+ value[op] = this.knex.raw(`(${fields.map(() => '?').join(', ')})`, tmp);
380
387
  }
381
388
  if (this.subQueries[key]) {
382
389
  return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), replacement, value[op]);
@@ -394,7 +401,8 @@ class QueryBuilderHelper {
394
401
  }));
395
402
  }
396
403
  else {
397
- qb[m](this.mapper(key, type, undefined, null), replacement, value[op]);
404
+ const mappedKey = this.mapper(key, type, value[op], null);
405
+ qb[m](mappedKey, replacement, value[op]);
398
406
  }
399
407
  }
400
408
  getOperatorReplacement(op, value) {
@@ -422,36 +430,58 @@ class QueryBuilderHelper {
422
430
  }
423
431
  getQueryOrderFromObject(type, orderBy, populate) {
424
432
  const ret = [];
425
- Object.keys(orderBy).forEach(k => {
426
- const direction = orderBy[k];
433
+ Object.keys(orderBy).forEach(key => {
434
+ const direction = orderBy[key];
427
435
  const order = core_1.Utils.isNumber(direction) ? core_1.QueryOrderNumeric[direction] : direction;
428
- if (QueryBuilderHelper.isCustomExpression(k)) {
429
- ret.push(`${k} ${order.toLowerCase()}`);
436
+ const raw = core_1.RawQueryFragment.getKnownFragment(key);
437
+ if (raw) {
438
+ ret.push(`${this.platform.formatQuery(raw.sql, raw.params)} ${order.toLowerCase()}`);
430
439
  return;
431
440
  }
432
- // eslint-disable-next-line prefer-const
433
- let [alias, field] = this.splitField(k, true);
434
- alias = populate[alias] || alias;
435
- core_1.Utils.splitPrimaryKeys(field).forEach(f => {
436
- const prop = this.getProperty(f, alias);
437
- const noPrefix = (prop && prop.persist === false && !prop.formula) || QueryBuilderHelper.isCustomExpression(f);
438
- const column = this.mapper(noPrefix ? f : `${alias}.${f}`, type, undefined, null);
441
+ core_1.Utils.splitPrimaryKeys(key).forEach(f => {
442
+ // eslint-disable-next-line prefer-const
443
+ let [alias, field] = this.splitField(f, true);
444
+ alias = populate[alias] || alias;
445
+ const prop = this.getProperty(field, alias);
446
+ const noPrefix = (prop && prop.persist === false && !prop.formula) || core_1.RawQueryFragment.isKnownFragment(f);
447
+ const column = this.mapper(noPrefix ? field : `${alias}.${field}`, type, undefined, null);
439
448
  /* istanbul ignore next */
440
449
  const rawColumn = core_1.Utils.isString(column) ? column.split('.').map(e => this.knex.ref(e)).join('.') : column;
441
450
  const customOrder = prop?.customOrder;
442
- const colPart = customOrder
451
+ let colPart = customOrder
443
452
  ? this.platform.generateCustomOrder(rawColumn, customOrder)
444
453
  : rawColumn;
454
+ if (core_1.Utils.isRawSql(colPart)) {
455
+ colPart = this.platform.formatQuery(colPart.sql, colPart.params);
456
+ }
445
457
  ret.push(`${colPart} ${order.toLowerCase()}`);
446
458
  });
447
459
  });
448
460
  return ret.join(', ');
449
461
  }
450
- finalize(type, qb, meta) {
451
- const useReturningStatement = type === enums_1.QueryType.INSERT && this.platform.usesReturningStatement() && meta && !meta.compositePK;
452
- if (useReturningStatement) {
453
- const returningProps = meta.hydrateProps.filter(prop => prop.persist !== false && (prop.primary || prop.defaultRaw));
454
- qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
462
+ finalize(type, qb, meta, data, returning) {
463
+ if (!meta || !data || !this.platform.usesReturningStatement()) {
464
+ return;
465
+ }
466
+ // always respect explicit returning hint
467
+ if (returning && returning.length > 0) {
468
+ qb.returning(returning.map(field => this.mapper(field, type)));
469
+ return;
470
+ }
471
+ if (type === enums_1.QueryType.INSERT) {
472
+ const returningProps = meta.hydrateProps
473
+ .filter(prop => prop.returning || (prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw)))
474
+ .filter(prop => !(prop.name in data));
475
+ if (returningProps.length > 0) {
476
+ qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
477
+ }
478
+ return;
479
+ }
480
+ if (type === enums_1.QueryType.UPDATE) {
481
+ const returningProps = meta.hydrateProps.filter(prop => core_1.Utils.isRawSql(data[prop.name]));
482
+ if (returningProps.length > 0) {
483
+ qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
484
+ }
455
485
  }
456
486
  }
457
487
  splitField(field, greedyAlias = false) {
@@ -489,23 +519,18 @@ class QueryBuilderHelper {
489
519
  }
490
520
  const versionProperty = meta.properties[meta.versionProperty];
491
521
  let sql = this.platform.quoteIdentifier(versionProperty.fieldNames[0]) + ' + 1';
492
- if (versionProperty.type.toLowerCase() === 'date') {
522
+ if (versionProperty.runtimeType === 'Date') {
493
523
  sql = this.platform.getCurrentTimestampSQL(versionProperty.length);
494
524
  }
495
525
  qb.update(versionProperty.fieldNames[0], this.knex.raw(sql));
496
526
  }
497
- static isCustomExpression(field, hasAlias = false) {
498
- // if we do not have alias, we don't consider spaces as custom expressions
499
- const re = hasAlias ? /[ ?<>=()'"`:]|^\d/ : /[?<>=()'"`:]|^\d/;
500
- return !!field.match(re);
501
- }
502
527
  prefix(field, always = false, quote = false) {
503
528
  let ret;
504
529
  if (!this.isPrefixed(field)) {
505
530
  const alias = always ? (quote ? this.alias : this.platform.quoteIdentifier(this.alias)) + '.' : '';
506
531
  const fieldName = this.fieldName(field, this.alias, always);
507
- if (QueryBuilderHelper.isCustomExpression(fieldName)) {
508
- return fieldName;
532
+ if (fieldName instanceof core_1.RawQueryFragment) {
533
+ return fieldName.sql;
509
534
  }
510
535
  ret = alias + fieldName;
511
536
  }
@@ -545,15 +570,15 @@ class QueryBuilderHelper {
545
570
  }
546
571
  if (prop.fieldNameRaw) {
547
572
  if (!always) {
548
- return prop.fieldNameRaw
549
- .replace(/\[::alias::]\.?/g, '')
550
- .replace(this.platform.quoteIdentifier('') + '.', '');
573
+ return (0, core_1.raw)(prop.fieldNameRaw
574
+ .replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE + '\\.?', 'g'), '')
575
+ .replace(this.platform.quoteIdentifier('') + '.', ''));
551
576
  }
552
577
  if (alias) {
553
- return prop.fieldNameRaw.replace(/\[::alias::]/g, alias);
578
+ return (0, core_1.raw)(prop.fieldNameRaw.replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE, 'g'), alias));
554
579
  }
555
580
  /* istanbul ignore next */
556
- return prop.fieldNameRaw;
581
+ return (0, core_1.raw)(prop.fieldNameRaw);
557
582
  }
558
583
  /* istanbul ignore next */
559
584
  return prop.fieldNames?.[0] ?? field;
@@ -564,7 +589,7 @@ class QueryBuilderHelper {
564
589
  // check if `alias` is not matching an embedded property name instead of alias, e.g. `address.city`
565
590
  if (alias && meta) {
566
591
  const prop = meta.properties[alias];
567
- if (prop?.reference === core_1.ReferenceType.EMBEDDED) {
592
+ if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
568
593
  // we want to select the full object property so hydration works as expected
569
594
  if (prop.object) {
570
595
  return prop;
@@ -596,17 +621,18 @@ class QueryBuilderHelper {
596
621
  return;
597
622
  }
598
623
  if (prop.joinColumns && Array.isArray(data[k])) {
599
- const copy = data[k];
624
+ const copy = core_1.Utils.flatten(data[k]);
600
625
  delete data[k];
601
626
  prop.joinColumns.forEach((joinColumn, idx) => data[joinColumn] = copy[idx]);
602
627
  return;
603
628
  }
604
629
  if (prop.customType && convertCustomTypes && !this.platform.isRaw(data[k])) {
605
- data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, { fromQuery: true, key: k, mode: 'query' });
630
+ data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, { fromQuery: true, key: k, mode: 'query-data' });
606
631
  }
607
- if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(data[k])) {
632
+ if (prop.hasConvertToDatabaseValueSQL && !this.platform.isRaw(data[k])) {
608
633
  const quoted = this.platform.quoteValue(data[k]);
609
- data[k] = this.knex.raw(prop.customType.convertToDatabaseValueSQL(quoted, this.platform).replace(/\?/, '\\?'));
634
+ const sql = prop.customType.convertToDatabaseValueSQL(quoted, this.platform);
635
+ data[k] = this.knex.raw(sql.replace(/\?/g, '\\?'));
610
636
  }
611
637
  if (!prop.customType && (Array.isArray(data[k]) || core_1.Utils.isPlainObject(data[k]))) {
612
638
  data[k] = JSON.stringify(data[k]);
@@ -3,7 +3,8 @@ import type { IQueryBuilder } from '../typings';
3
3
  /**
4
4
  * @internal
5
5
  */
6
- export declare class ScalarCriteriaNode extends CriteriaNode {
7
- process<T>(qb: IQueryBuilder<T>, alias?: string): any;
6
+ export declare class ScalarCriteriaNode<T extends object> extends CriteriaNode<T> {
7
+ process(qb: IQueryBuilder<T>, alias?: string): any;
8
+ willAutoJoin<T>(qb: IQueryBuilder<T>, alias?: string): boolean;
8
9
  shouldJoin(): boolean;
9
10
  }
@@ -13,23 +13,26 @@ class ScalarCriteriaNode extends CriteriaNode_1.CriteriaNode {
13
13
  const parentPath = this.parent.getPath(); // the parent is always there, otherwise `shouldJoin` would return `false`
14
14
  const nestedAlias = qb.getAliasForJoinPath(path) || qb.getNextAlias(this.prop?.pivotTable ?? this.entityName);
15
15
  const field = `${alias}.${this.prop.name}`;
16
- const type = this.prop.reference === core_1.ReferenceType.MANY_TO_MANY ? 'pivotJoin' : 'leftJoin';
16
+ const type = this.prop.kind === core_1.ReferenceKind.MANY_TO_MANY ? 'pivotJoin' : 'leftJoin';
17
17
  qb.join(field, nestedAlias, undefined, type, path);
18
18
  // select the owner as virtual property when joining from 1:1 inverse side, but only if the parent is root entity
19
- if (this.prop.reference === core_1.ReferenceType.ONE_TO_ONE && !parentPath.includes('.')) {
19
+ if (this.prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !parentPath.includes('.')) {
20
20
  qb.addSelect(field);
21
21
  }
22
22
  }
23
23
  return this.payload;
24
24
  }
25
+ willAutoJoin(qb, alias) {
26
+ return this.shouldJoin();
27
+ }
25
28
  shouldJoin() {
26
29
  if (!this.parent || !this.prop) {
27
30
  return false;
28
31
  }
29
- switch (this.prop.reference) {
30
- case core_1.ReferenceType.ONE_TO_MANY: return true;
31
- case core_1.ReferenceType.MANY_TO_MANY: return true;
32
- case core_1.ReferenceType.ONE_TO_ONE: return !this.prop.owner;
32
+ switch (this.prop.kind) {
33
+ case core_1.ReferenceKind.ONE_TO_MANY: return true;
34
+ case core_1.ReferenceKind.MANY_TO_MANY: return true;
35
+ case core_1.ReferenceKind.ONE_TO_ONE: return !this.prop.owner;
33
36
  default: return false; // SCALAR, MANY_TO_ONE
34
37
  }
35
38
  }
package/query/enums.js CHANGED
@@ -9,4 +9,4 @@ var QueryType;
9
9
  QueryType["INSERT"] = "INSERT";
10
10
  QueryType["UPDATE"] = "UPDATE";
11
11
  QueryType["DELETE"] = "DELETE";
12
- })(QueryType = exports.QueryType || (exports.QueryType = {}));
12
+ })(QueryType || (exports.QueryType = QueryType = {}));
@@ -1,4 +1,4 @@
1
- import type { Configuration, Dictionary, EntityMetadata } from '@mikro-orm/core';
1
+ import { type Configuration, type Dictionary, type EntityMetadata } from '@mikro-orm/core';
2
2
  import { DatabaseTable } from './DatabaseTable';
3
3
  import type { AbstractSqlConnection } from '../AbstractSqlConnection';
4
4
  import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
@@ -10,14 +10,17 @@ export declare class DatabaseSchema {
10
10
  readonly name: string;
11
11
  private tables;
12
12
  private namespaces;
13
+ private nativeEnums;
13
14
  constructor(platform: AbstractSqlPlatform, name: string);
14
15
  addTable(name: string, schema: string | undefined | null, comment?: string): DatabaseTable;
15
16
  getTables(): DatabaseTable[];
16
17
  getTable(name: string): DatabaseTable | undefined;
17
18
  hasTable(name: string): boolean;
19
+ setNativeEnums(nativeEnums: Dictionary<unknown[]>): void;
20
+ getNativeEnums(): Dictionary<unknown[]>;
18
21
  hasNamespace(namespace: string): boolean;
19
22
  getNamespaces(): string[];
20
- static create(connection: AbstractSqlConnection, platform: AbstractSqlPlatform, config: Configuration, schemaName?: string): Promise<DatabaseSchema>;
23
+ static create(connection: AbstractSqlConnection, platform: AbstractSqlPlatform, config: Configuration, schemaName?: string, schemas?: string[]): Promise<DatabaseSchema>;
21
24
  static fromMetadata(metadata: EntityMetadata[], platform: AbstractSqlPlatform, config: Configuration, schemaName?: string): DatabaseSchema;
22
25
  private static getSchemaName;
23
26
  private static shouldHaveColumn;