@mikro-orm/knex 6.0.0-dev.8 → 6.0.0-dev.80

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 (38) hide show
  1. package/AbstractSqlConnection.d.ts +5 -2
  2. package/AbstractSqlConnection.js +25 -17
  3. package/AbstractSqlDriver.d.ts +18 -18
  4. package/AbstractSqlDriver.js +117 -48
  5. package/AbstractSqlPlatform.d.ts +1 -0
  6. package/AbstractSqlPlatform.js +20 -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 -5
  11. package/SqlEntityManager.js +5 -9
  12. package/SqlEntityRepository.d.ts +6 -4
  13. package/SqlEntityRepository.js +10 -8
  14. package/index.mjs +19 -6
  15. package/package.json +7 -7
  16. package/query/ArrayCriteriaNode.d.ts +3 -3
  17. package/query/CriteriaNode.d.ts +8 -8
  18. package/query/CriteriaNode.js +9 -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 +25 -7
  24. package/query/QueryBuilder.js +237 -61
  25. package/query/QueryBuilderHelper.d.ts +3 -5
  26. package/query/QueryBuilderHelper.js +96 -74
  27. package/query/ScalarCriteriaNode.d.ts +3 -2
  28. package/query/ScalarCriteriaNode.js +9 -6
  29. package/schema/DatabaseSchema.d.ts +4 -1
  30. package/schema/DatabaseSchema.js +22 -6
  31. package/schema/DatabaseTable.d.ts +2 -1
  32. package/schema/DatabaseTable.js +25 -24
  33. package/schema/SchemaComparator.js +9 -2
  34. package/schema/SchemaHelper.d.ts +5 -3
  35. package/schema/SchemaHelper.js +10 -4
  36. package/schema/SqlSchemaGenerator.d.ts +3 -5
  37. package/schema/SqlSchemaGenerator.js +41 -20
  38. package/typings.d.ts +8 -5
@@ -19,6 +19,10 @@ class QueryBuilderHelper {
19
19
  this.metadata = this.driver.getMetadata();
20
20
  }
21
21
  mapper(field, type = enums_1.QueryType.SELECT, value, alias) {
22
+ if (core_1.Utils.isRawSql(field)) {
23
+ return this.knex.raw(field.sql, field.params);
24
+ }
25
+ /* istanbul ignore next */
22
26
  if (typeof field !== 'string') {
23
27
  return field;
24
28
  }
@@ -36,12 +40,26 @@ class QueryBuilderHelper {
36
40
  parts.push(this.mapper(a !== this.alias ? `${a}.${f}` : f, type, value, alias));
37
41
  }
38
42
  }
43
+ // flatten the value if we see we are expanding nested composite key
44
+ // hackish, but cleaner solution would require quite a lot of refactoring
45
+ if (fields.length !== parts.length && Array.isArray(value)) {
46
+ value.forEach(row => {
47
+ if (Array.isArray(row)) {
48
+ const tmp = core_1.Utils.flatten(row);
49
+ row.length = 0;
50
+ row.push(...tmp);
51
+ }
52
+ });
53
+ }
39
54
  return this.knex.raw('(' + parts.map(part => this.knex.ref(part)).join(', ') + ')');
40
55
  }
41
- let ret = field;
42
- const customExpression = QueryBuilderHelper.isCustomExpression(field, !!alias);
56
+ const rawField = core_1.RawQueryFragment.getKnownFragment(field);
57
+ if (rawField) {
58
+ return this.knex.raw(rawField.sql, rawField.params);
59
+ }
43
60
  const [a, f] = this.splitField(field);
44
61
  const prop = this.getProperty(f, a);
62
+ let ret = field;
45
63
  // embeddable nested path instead of a regular property with table alias, reset alias
46
64
  if (prop?.name === a && prop.embeddedProps[f]) {
47
65
  return this.alias + '.' + prop.fieldNames[0];
@@ -56,7 +74,7 @@ class QueryBuilderHelper {
56
74
  const as = alias === null ? '' : ` as ${aliased}`;
57
75
  return this.knex.raw(`${prop.formula(alias2)}${as}`);
58
76
  }
59
- if (prop?.customType?.convertToJSValueSQL) {
77
+ if (prop?.hasConvertToJSValueSQL) {
60
78
  const prefixed = this.prefix(field, isTableNameAliasRequired, true);
61
79
  const valueSQL = prop.customType.convertToJSValueSQL(prefixed, this.platform);
62
80
  if (alias === null) {
@@ -65,15 +83,12 @@ class QueryBuilderHelper {
65
83
  return this.knex.raw(`${valueSQL} as ${this.platform.quoteIdentifier(alias ?? prop.fieldNames[0])}`);
66
84
  }
67
85
  // do not wrap custom expressions
68
- if (!customExpression) {
86
+ if (!rawField) {
69
87
  ret = this.prefix(field);
70
88
  }
71
89
  if (alias) {
72
90
  ret += ' as ' + alias;
73
91
  }
74
- if (customExpression) {
75
- return this.knex.raw(ret, value);
76
- }
77
92
  if (!isTableNameAliasRequired || this.isPrefixed(ret) || noPrefix) {
78
93
  return ret;
79
94
  }
@@ -162,6 +177,11 @@ class QueryBuilderHelper {
162
177
  const right = `${join.alias}.${join.joinColumns[idx]}`;
163
178
  conditions.push(`${this.knex.ref(left)} = ${this.knex.ref(right)}`);
164
179
  });
180
+ if (join.prop.targetMeta.discriminatorValue && !join.path?.endsWith('[pivot]')) {
181
+ const typeProperty = join.prop.targetMeta.root.discriminatorColumn;
182
+ const alias = !join.prop.owner ? join.inverseAlias ?? join.alias : join.ownerAlias;
183
+ join.cond[`${alias}.${typeProperty}`] = join.prop.targetMeta.discriminatorValue;
184
+ }
165
185
  Object.keys(join.cond).forEach(key => {
166
186
  conditions.push(this.processJoinClause(key, join.cond[key], params));
167
187
  });
@@ -212,20 +232,14 @@ class QueryBuilderHelper {
212
232
  if (operator === '$exists') {
213
233
  value = null;
214
234
  }
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 + ' = ?';
235
+ const rawField = core_1.RawQueryFragment.getKnownFragment(key);
236
+ if (rawField) {
237
+ let sql = rawField.sql;
238
+ params.push(...rawField.params);
239
+ params.push(...core_1.Utils.asArray(value));
240
+ if (core_1.Utils.asArray(value).length > 0) {
241
+ sql += ' = ?';
227
242
  }
228
- params.push(...bindings);
229
243
  return sql;
230
244
  }
231
245
  const sql = this.mapper(key);
@@ -241,7 +255,7 @@ class QueryBuilderHelper {
241
255
  return `(${parts.join(` ${core_1.GroupOperator[operator]} `)})`;
242
256
  }
243
257
  mapJoinColumns(type, join) {
244
- if (join.prop && join.prop.reference === core_1.ReferenceType.ONE_TO_ONE && !join.prop.owner) {
258
+ if (join.prop && join.prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !join.prop.owner) {
245
259
  return join.prop.fieldNames.map((fieldName, idx) => {
246
260
  return this.mapper(`${join.alias}.${join.inverseJoinColumns[idx]}`, type, undefined, fieldName);
247
261
  });
@@ -254,7 +268,7 @@ class QueryBuilderHelper {
254
268
  isOneToOneInverse(field) {
255
269
  const meta = this.metadata.find(this.entityName);
256
270
  const prop = meta.properties[field];
257
- return prop && prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner;
271
+ return prop && prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner;
258
272
  }
259
273
  getTableName(entityName) {
260
274
  const meta = this.metadata.find(entityName);
@@ -293,13 +307,13 @@ class QueryBuilderHelper {
293
307
  }
294
308
  appendOnConflictClause(type, onConflict, qb) {
295
309
  onConflict.forEach(item => {
296
- const sub = qb.onConflict(item.fields);
310
+ const sub = item.fields.length > 0 ? qb.onConflict(item.fields) : qb.onConflict();
297
311
  core_1.Utils.runIfNotEmpty(() => sub.ignore(), item.ignore);
298
312
  core_1.Utils.runIfNotEmpty(() => {
299
313
  let mergeParam = item.merge;
300
314
  if (core_1.Utils.isObject(item.merge)) {
301
315
  mergeParam = {};
302
- Object.keys(item.merge).forEach(key => {
316
+ core_1.Utils.keys(item.merge).forEach(key => {
303
317
  const k = this.mapper(key, type);
304
318
  mergeParam[k] = item.merge[key];
305
319
  });
@@ -336,28 +350,20 @@ class QueryBuilderHelper {
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];
@@ -394,7 +400,8 @@ class QueryBuilderHelper {
394
400
  }));
395
401
  }
396
402
  else {
397
- qb[m](this.mapper(key, type, undefined, null), replacement, value[op]);
403
+ const mappedKey = this.mapper(key, type, value[op], null);
404
+ qb[m](mappedKey, replacement, value[op]);
398
405
  }
399
406
  }
400
407
  getOperatorReplacement(op, value) {
@@ -422,20 +429,21 @@ class QueryBuilderHelper {
422
429
  }
423
430
  getQueryOrderFromObject(type, orderBy, populate) {
424
431
  const ret = [];
425
- Object.keys(orderBy).forEach(k => {
426
- const direction = orderBy[k];
432
+ Object.keys(orderBy).forEach(key => {
433
+ const direction = orderBy[key];
427
434
  const order = core_1.Utils.isNumber(direction) ? core_1.QueryOrderNumeric[direction] : direction;
428
- if (QueryBuilderHelper.isCustomExpression(k)) {
429
- ret.push(`${k} ${order.toLowerCase()}`);
435
+ const raw = core_1.RawQueryFragment.getKnownFragment(key);
436
+ if (raw) {
437
+ ret.push(`${this.platform.formatQuery(raw.sql, raw.params)} ${order.toLowerCase()}`);
430
438
  return;
431
439
  }
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);
440
+ core_1.Utils.splitPrimaryKeys(key).forEach(f => {
441
+ // eslint-disable-next-line prefer-const
442
+ let [alias, field] = this.splitField(f, true);
443
+ alias = populate[alias] || alias;
444
+ const prop = this.getProperty(field, alias);
445
+ const noPrefix = (prop && prop.persist === false && !prop.formula) || core_1.RawQueryFragment.isKnownFragment(f);
446
+ const column = this.mapper(noPrefix ? field : `${alias}.${field}`, type, undefined, null);
439
447
  /* istanbul ignore next */
440
448
  const rawColumn = core_1.Utils.isString(column) ? column.split('.').map(e => this.knex.ref(e)).join('.') : column;
441
449
  const customOrder = prop?.customOrder;
@@ -447,11 +455,29 @@ class QueryBuilderHelper {
447
455
  });
448
456
  return ret.join(', ');
449
457
  }
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)));
458
+ finalize(type, qb, meta, data, returning) {
459
+ if (!meta || !data || !this.platform.usesReturningStatement()) {
460
+ return;
461
+ }
462
+ // always respect explicit returning hint
463
+ if (returning && returning.length > 0) {
464
+ qb.returning(returning.map(field => this.mapper(field, type)));
465
+ return;
466
+ }
467
+ if (type === enums_1.QueryType.INSERT) {
468
+ const returningProps = meta.hydrateProps
469
+ .filter(prop => prop.returning || (prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw)))
470
+ .filter(prop => !(prop.name in data));
471
+ if (returningProps.length > 0) {
472
+ qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
473
+ }
474
+ return;
475
+ }
476
+ if (type === enums_1.QueryType.UPDATE) {
477
+ const returningProps = meta.hydrateProps.filter(prop => core_1.Utils.isRawSql(data[prop.name]));
478
+ if (returningProps.length > 0) {
479
+ qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
480
+ }
455
481
  }
456
482
  }
457
483
  splitField(field, greedyAlias = false) {
@@ -494,18 +520,13 @@ class QueryBuilderHelper {
494
520
  }
495
521
  qb.update(versionProperty.fieldNames[0], this.knex.raw(sql));
496
522
  }
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
523
  prefix(field, always = false, quote = false) {
503
524
  let ret;
504
525
  if (!this.isPrefixed(field)) {
505
526
  const alias = always ? (quote ? this.alias : this.platform.quoteIdentifier(this.alias)) + '.' : '';
506
527
  const fieldName = this.fieldName(field, this.alias, always);
507
- if (QueryBuilderHelper.isCustomExpression(fieldName)) {
508
- return fieldName;
528
+ if (fieldName instanceof core_1.RawQueryFragment) {
529
+ return fieldName.sql;
509
530
  }
510
531
  ret = alias + fieldName;
511
532
  }
@@ -545,15 +566,15 @@ class QueryBuilderHelper {
545
566
  }
546
567
  if (prop.fieldNameRaw) {
547
568
  if (!always) {
548
- return prop.fieldNameRaw
549
- .replace(/\[::alias::]\.?/g, '')
550
- .replace(this.platform.quoteIdentifier('') + '.', '');
569
+ return (0, core_1.raw)(prop.fieldNameRaw
570
+ .replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE + '\\.?', 'g'), '')
571
+ .replace(this.platform.quoteIdentifier('') + '.', ''));
551
572
  }
552
573
  if (alias) {
553
- return prop.fieldNameRaw.replace(/\[::alias::]/g, alias);
574
+ return (0, core_1.raw)(prop.fieldNameRaw.replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE, 'g'), alias));
554
575
  }
555
576
  /* istanbul ignore next */
556
- return prop.fieldNameRaw;
577
+ return (0, core_1.raw)(prop.fieldNameRaw);
557
578
  }
558
579
  /* istanbul ignore next */
559
580
  return prop.fieldNames?.[0] ?? field;
@@ -564,7 +585,7 @@ class QueryBuilderHelper {
564
585
  // check if `alias` is not matching an embedded property name instead of alias, e.g. `address.city`
565
586
  if (alias && meta) {
566
587
  const prop = meta.properties[alias];
567
- if (prop?.reference === core_1.ReferenceType.EMBEDDED) {
588
+ if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
568
589
  // we want to select the full object property so hydration works as expected
569
590
  if (prop.object) {
570
591
  return prop;
@@ -596,17 +617,18 @@ class QueryBuilderHelper {
596
617
  return;
597
618
  }
598
619
  if (prop.joinColumns && Array.isArray(data[k])) {
599
- const copy = data[k];
620
+ const copy = core_1.Utils.flatten(data[k]);
600
621
  delete data[k];
601
622
  prop.joinColumns.forEach((joinColumn, idx) => data[joinColumn] = copy[idx]);
602
623
  return;
603
624
  }
604
625
  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' });
626
+ data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, { fromQuery: true, key: k, mode: 'query-data' });
606
627
  }
607
- if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(data[k])) {
628
+ if (prop.hasConvertToDatabaseValueSQL && !this.platform.isRaw(data[k])) {
608
629
  const quoted = this.platform.quoteValue(data[k]);
609
- data[k] = this.knex.raw(prop.customType.convertToDatabaseValueSQL(quoted, this.platform).replace(/\?/, '\\?'));
630
+ const sql = prop.customType.convertToDatabaseValueSQL(quoted, this.platform);
631
+ data[k] = this.knex.raw(sql.replace(/\?/, '\\?'));
610
632
  }
611
633
  if (!prop.customType && (Array.isArray(data[k]) || core_1.Utils.isPlainObject(data[k]))) {
612
634
  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
  }
@@ -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;
@@ -12,10 +12,12 @@ class DatabaseSchema {
12
12
  this.name = name;
13
13
  this.tables = [];
14
14
  this.namespaces = new Set();
15
+ this.nativeEnums = {}; // for postgres
15
16
  }
16
17
  addTable(name, schema, comment) {
17
18
  const namespaceName = schema ?? this.name;
18
19
  const table = new DatabaseTable_1.DatabaseTable(this.platform, name, namespaceName);
20
+ table.nativeEnums = this.nativeEnums;
19
21
  table.comment = comment;
20
22
  this.tables.push(table);
21
23
  if (namespaceName != null) {
@@ -32,24 +34,38 @@ class DatabaseSchema {
32
34
  hasTable(name) {
33
35
  return !!this.getTable(name);
34
36
  }
37
+ setNativeEnums(nativeEnums) {
38
+ this.nativeEnums = nativeEnums;
39
+ this.tables.forEach(t => t.nativeEnums = nativeEnums);
40
+ }
41
+ getNativeEnums() {
42
+ return this.nativeEnums;
43
+ }
35
44
  hasNamespace(namespace) {
36
45
  return this.namespaces.has(namespace);
37
46
  }
38
47
  getNamespaces() {
39
48
  return [...this.namespaces];
40
49
  }
41
- static async create(connection, platform, config, schemaName) {
42
- const schema = new DatabaseSchema(platform, schemaName ?? config.get('schema'));
50
+ static async create(connection, platform, config, schemaName, schemas) {
51
+ const schema = new DatabaseSchema(platform, schemaName ?? config.get('schema') ?? platform.getDefaultSchemaName());
43
52
  const allTables = await connection.execute(platform.getSchemaHelper().getListTablesSQL());
44
53
  const parts = config.get('migrations').tableName.split('.');
45
54
  const migrationsTableName = parts[1] ?? parts[0];
46
55
  const migrationsSchemaName = parts.length > 1 ? parts[0] : config.get('schema', platform.getDefaultSchemaName());
47
56
  const tables = allTables.filter(t => t.table_name !== migrationsTableName || (t.schema_name && t.schema_name !== migrationsSchemaName));
48
- await platform.getSchemaHelper().loadInformationSchema(schema, connection, tables);
57
+ await platform.getSchemaHelper().loadInformationSchema(schema, connection, tables, schemas && schemas.length > 0 ? schemas : undefined);
49
58
  return schema;
50
59
  }
51
60
  static fromMetadata(metadata, platform, config, schemaName) {
52
61
  const schema = new DatabaseSchema(platform, schemaName ?? config.get('schema'));
62
+ const nativeEnums = {};
63
+ for (const meta of metadata) {
64
+ meta.props
65
+ .filter(prop => prop.nativeEnumName)
66
+ .forEach(prop => nativeEnums[prop.nativeEnumName] = prop.items?.map(val => '' + val) ?? []);
67
+ }
68
+ schema.setNativeEnums(nativeEnums);
53
69
  for (const meta of metadata) {
54
70
  const table = schema.addTable(meta.collection, this.getSchemaName(meta, config, schemaName));
55
71
  table.comment = meta.comment;
@@ -78,15 +94,15 @@ class DatabaseSchema {
78
94
  if (prop.persist === false || !prop.fieldNames) {
79
95
  return false;
80
96
  }
81
- if (meta.pivotTable || (core_1.ReferenceType.EMBEDDED && prop.object)) {
97
+ if (meta.pivotTable || (core_1.ReferenceKind.EMBEDDED && prop.object)) {
82
98
  return true;
83
99
  }
84
100
  const getRootProperty = (prop) => prop.embedded ? getRootProperty(meta.properties[prop.embedded[0]]) : prop;
85
101
  const rootProp = getRootProperty(prop);
86
- if (rootProp.reference === core_1.ReferenceType.EMBEDDED) {
102
+ if (rootProp.kind === core_1.ReferenceKind.EMBEDDED) {
87
103
  return prop === rootProp || !rootProp.object;
88
104
  }
89
- return [core_1.ReferenceType.SCALAR, core_1.ReferenceType.MANY_TO_ONE].includes(prop.reference) || (prop.reference === core_1.ReferenceType.ONE_TO_ONE && prop.owner);
105
+ return [core_1.ReferenceKind.SCALAR, core_1.ReferenceKind.MANY_TO_ONE].includes(prop.kind) || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && prop.owner);
90
106
  }
91
107
  toJSON() {
92
108
  const { platform, namespaces, ...rest } = this;
@@ -13,6 +13,7 @@ export declare class DatabaseTable {
13
13
  private indexes;
14
14
  private checks;
15
15
  private foreignKeys;
16
+ nativeEnums: Dictionary<unknown[]>;
16
17
  comment?: string;
17
18
  constructor(platform: AbstractSqlPlatform, name: string, schema?: string | undefined);
18
19
  getColumns(): Column[];
@@ -37,7 +38,7 @@ export declare class DatabaseTable {
37
38
  getPrimaryKey(): IndexDef | undefined;
38
39
  hasPrimaryKey(): boolean;
39
40
  private getPropertyDeclaration;
40
- private getReferenceType;
41
+ private getReferenceKind;
41
42
  private getPropertyName;
42
43
  private getPropertyType;
43
44
  private getPropertyDefaultValue;
@@ -14,6 +14,7 @@ class DatabaseTable {
14
14
  this.indexes = [];
15
15
  this.checks = [];
16
16
  this.foreignKeys = {};
17
+ this.nativeEnums = {}; // for postgres
17
18
  Object.defineProperties(this, {
18
19
  platform: { enumerable: false, writable: true },
19
20
  });
@@ -41,7 +42,7 @@ class DatabaseTable {
41
42
  const type = v.name in enums ? 'enum' : v.type;
42
43
  v.mappedType = this.platform.getMappedType(type);
43
44
  v.default = v.default?.toString().startsWith('nextval(') ? null : v.default;
44
- v.enumItems = enums[v.name] || [];
45
+ v.enumItems ??= enums[v.name] || [];
45
46
  o[v.name] = v;
46
47
  return o;
47
48
  }, {});
@@ -51,7 +52,6 @@ class DatabaseTable {
51
52
  }
52
53
  addColumnFromProperty(prop, meta) {
53
54
  prop.fieldNames.forEach((field, idx) => {
54
- var _a;
55
55
  const type = prop.enum ? 'enum' : prop.columnTypes[idx];
56
56
  const mappedType = this.platform.getMappedType(type);
57
57
  if (mappedType instanceof core_1.DecimalType) {
@@ -64,9 +64,9 @@ class DatabaseTable {
64
64
  }
65
65
  }
66
66
  if (mappedType instanceof core_1.DateTimeType) {
67
- prop.length ?? (prop.length = this.platform.getDefaultDateTimeLength());
67
+ prop.length ??= this.platform.getDefaultDateTimeLength();
68
68
  }
69
- const primary = !meta.compositePK && !!prop.primary && prop.reference === core_1.ReferenceType.SCALAR && this.platform.isNumericColumn(mappedType);
69
+ const primary = !meta.compositePK && !!prop.primary && prop.kind === core_1.ReferenceKind.SCALAR && this.platform.isNumericColumn(mappedType);
70
70
  this.columns[field] = {
71
71
  name: prop.fieldNames[idx],
72
72
  type: prop.columnTypes[idx],
@@ -74,21 +74,22 @@ class DatabaseTable {
74
74
  unsigned: prop.unsigned && this.platform.isNumericColumn(mappedType),
75
75
  autoincrement: prop.autoincrement ?? primary,
76
76
  primary,
77
+ nativeEnumName: prop.nativeEnumName,
77
78
  nullable: !!prop.nullable,
78
79
  length: prop.length,
79
80
  precision: prop.precision,
80
81
  scale: prop.scale,
81
82
  default: prop.defaultRaw,
82
- enumItems: prop.items?.every(core_1.Utils.isString) ? prop.items : undefined,
83
+ enumItems: prop.nativeEnumName || prop.items?.every(core_1.Utils.isString) ? prop.items : undefined,
83
84
  comment: prop.comment,
84
85
  extra: prop.extra,
85
86
  ignoreSchemaChanges: prop.ignoreSchemaChanges,
86
87
  };
87
- (_a = this.columns[field]).unsigned || (_a.unsigned = this.columns[field].autoincrement);
88
+ this.columns[field].unsigned ||= this.columns[field].autoincrement;
88
89
  const defaultValue = this.platform.getSchemaHelper().normalizeDefaultValue(prop.defaultRaw, prop.length);
89
90
  this.columns[field].default = defaultValue;
90
91
  });
91
- if ([core_1.ReferenceType.MANY_TO_ONE, core_1.ReferenceType.ONE_TO_ONE].includes(prop.reference)) {
92
+ if ([core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
92
93
  const constraintName = this.getIndexName(true, prop.fieldNames, 'foreign');
93
94
  let schema = prop.targetMeta.schema === '*' ? this.schema : prop.targetMeta.schema ?? this.schema;
94
95
  if (prop.referencedTableName.includes('.')) {
@@ -102,11 +103,11 @@ class DatabaseTable {
102
103
  referencedTableName: schema ? `${schema}.${prop.referencedTableName}` : prop.referencedTableName,
103
104
  };
104
105
  const cascade = prop.cascade.includes(core_1.Cascade.REMOVE) || prop.cascade.includes(core_1.Cascade.ALL);
105
- if (prop.onDelete || cascade || prop.nullable) {
106
- this.foreignKeys[constraintName].deleteRule = prop.onDelete || (cascade ? 'cascade' : 'set null');
106
+ if (prop.deleteRule || cascade || prop.nullable) {
107
+ this.foreignKeys[constraintName].deleteRule = prop.deleteRule || (cascade ? 'cascade' : 'set null');
107
108
  }
108
- if (prop.onUpdateIntegrity || prop.cascade.includes(core_1.Cascade.PERSIST) || prop.cascade.includes(core_1.Cascade.ALL)) {
109
- this.foreignKeys[constraintName].updateRule = prop.onUpdateIntegrity || 'cascade';
109
+ if (prop.updateRule || prop.cascade.includes(core_1.Cascade.PERSIST) || prop.cascade.includes(core_1.Cascade.ALL)) {
110
+ this.foreignKeys[constraintName].updateRule = prop.updateRule || 'cascade';
110
111
  }
111
112
  }
112
113
  if (prop.index) {
@@ -164,8 +165,8 @@ class DatabaseTable {
164
165
  }
165
166
  const meta = schema.init().meta;
166
167
  meta.relations
167
- .filter(prop => prop.primary && prop.reference === core_1.ReferenceType.MANY_TO_ONE && !meta.compositePK)
168
- .forEach(prop => prop.reference = core_1.ReferenceType.ONE_TO_ONE);
168
+ .filter(prop => prop.primary && prop.kind === core_1.ReferenceKind.MANY_TO_ONE && !meta.compositePK)
169
+ .forEach(prop => prop.kind = core_1.ReferenceKind.ONE_TO_ONE);
169
170
  return meta;
170
171
  }
171
172
  /**
@@ -206,20 +207,20 @@ class DatabaseTable {
206
207
  const prop = this.getPropertyName(namingStrategy, column);
207
208
  const index = compositeFkIndexes[prop] || this.indexes.find(idx => idx.columnNames[0] === column.name && !idx.composite && !idx.unique && !idx.primary);
208
209
  const unique = compositeFkUniques[prop] || this.indexes.find(idx => idx.columnNames[0] === column.name && !idx.composite && idx.unique && !idx.primary);
209
- const reference = this.getReferenceType(fk, unique);
210
+ const kind = this.getReferenceKind(fk, unique);
210
211
  const type = this.getPropertyType(namingStrategy, column, fk);
211
212
  const fkOptions = {};
212
213
  if (fk) {
213
214
  fkOptions.fieldNames = fk.columnNames;
214
215
  fkOptions.referencedTableName = fk.referencedTableName;
215
216
  fkOptions.referencedColumnNames = fk.referencedColumnNames;
216
- fkOptions.onUpdateIntegrity = fk.updateRule?.toLowerCase();
217
- fkOptions.onDelete = fk.deleteRule?.toLowerCase();
217
+ fkOptions.updateRule = fk.updateRule?.toLowerCase();
218
+ fkOptions.deleteRule = fk.deleteRule?.toLowerCase();
218
219
  }
219
220
  return {
220
221
  name: prop,
221
222
  type,
222
- reference,
223
+ kind,
223
224
  columnType: column.type,
224
225
  default: this.getPropertyDefaultValue(schemaHelper, column, type),
225
226
  defaultRaw: this.getPropertyDefaultValue(schemaHelper, column, type, true),
@@ -234,14 +235,14 @@ class DatabaseTable {
234
235
  ...fkOptions,
235
236
  };
236
237
  }
237
- getReferenceType(fk, unique) {
238
+ getReferenceKind(fk, unique) {
238
239
  if (fk && unique) {
239
- return core_1.ReferenceType.ONE_TO_ONE;
240
+ return core_1.ReferenceKind.ONE_TO_ONE;
240
241
  }
241
242
  if (fk) {
242
- return core_1.ReferenceType.MANY_TO_ONE;
243
+ return core_1.ReferenceKind.MANY_TO_ONE;
243
244
  }
244
- return core_1.ReferenceType.SCALAR;
245
+ return core_1.ReferenceKind.SCALAR;
245
246
  }
246
247
  getPropertyName(namingStrategy, column) {
247
248
  const fk = Object.values(this.foreignKeys).find(fk => fk.columnNames.includes(column.name));
@@ -288,7 +289,7 @@ class DatabaseTable {
288
289
  return '' + val;
289
290
  }
290
291
  addIndex(meta, index, type) {
291
- const properties = core_1.Utils.flatten(core_1.Utils.asArray(index.properties).map(prop => meta.properties[prop].fieldNames));
292
+ const properties = core_1.Utils.unique(core_1.Utils.flatten(core_1.Utils.asArray(index.properties).map(prop => meta.properties[prop].fieldNames)));
292
293
  if (properties.length === 0 && !index.expression) {
293
294
  return;
294
295
  }
@@ -308,10 +309,10 @@ class DatabaseTable {
308
309
  }
309
310
  toJSON() {
310
311
  const { platform, columns, ...rest } = this;
311
- const columnsMapped = Object.keys(columns).reduce((o, col) => {
312
+ const columnsMapped = core_1.Utils.keys(columns).reduce((o, col) => {
312
313
  const { mappedType, ...restCol } = columns[col];
313
314
  o[col] = restCol;
314
- o[col].mappedType = Object.keys(core_1.t).find(k => core_1.t[k] === mappedType.constructor);
315
+ o[col].mappedType = core_1.Utils.keys(core_1.t).find(k => core_1.t[k] === mappedType.constructor);
315
316
  return o;
316
317
  }, {});
317
318
  return { columns: columnsMapped, ...rest };