@mikro-orm/sql 7.0.0-rc.1 → 7.0.0-rc.3

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 (47) hide show
  1. package/AbstractSqlConnection.js +2 -1
  2. package/AbstractSqlDriver.d.ts +18 -12
  3. package/AbstractSqlDriver.js +187 -38
  4. package/AbstractSqlPlatform.d.ts +1 -0
  5. package/AbstractSqlPlatform.js +5 -3
  6. package/PivotCollectionPersister.js +2 -2
  7. package/SqlEntityManager.d.ts +5 -4
  8. package/SqlEntityManager.js +5 -5
  9. package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +2 -0
  10. package/dialects/mssql/MsSqlNativeQueryBuilder.js +8 -4
  11. package/dialects/mysql/BaseMySqlPlatform.js +1 -2
  12. package/dialects/mysql/MySqlSchemaHelper.js +21 -10
  13. package/dialects/postgresql/BasePostgreSqlPlatform.js +38 -30
  14. package/dialects/postgresql/PostgreSqlSchemaHelper.js +63 -47
  15. package/dialects/sqlite/BaseSqliteConnection.d.ts +4 -1
  16. package/dialects/sqlite/BaseSqliteConnection.js +4 -0
  17. package/dialects/sqlite/NodeSqliteDialect.d.ts +21 -0
  18. package/dialects/sqlite/NodeSqliteDialect.js +43 -0
  19. package/dialects/sqlite/SqliteDriver.d.ts +12 -0
  20. package/dialects/sqlite/SqliteDriver.js +14 -0
  21. package/dialects/sqlite/{BaseSqlitePlatform.d.ts → SqlitePlatform.d.ts} +5 -2
  22. package/dialects/sqlite/{BaseSqlitePlatform.js → SqlitePlatform.js} +30 -4
  23. package/dialects/sqlite/SqliteSchemaHelper.d.ts +5 -0
  24. package/dialects/sqlite/SqliteSchemaHelper.js +31 -10
  25. package/dialects/sqlite/index.d.ts +3 -1
  26. package/dialects/sqlite/index.js +3 -1
  27. package/package.json +30 -30
  28. package/plugin/transformer.js +17 -16
  29. package/query/CriteriaNode.js +28 -10
  30. package/query/CriteriaNodeFactory.js +5 -1
  31. package/query/NativeQueryBuilder.d.ts +25 -0
  32. package/query/NativeQueryBuilder.js +61 -1
  33. package/query/ObjectCriteriaNode.js +71 -27
  34. package/query/QueryBuilder.d.ts +177 -48
  35. package/query/QueryBuilder.js +233 -54
  36. package/query/QueryBuilderHelper.d.ts +4 -3
  37. package/query/QueryBuilderHelper.js +47 -17
  38. package/query/ScalarCriteriaNode.js +14 -7
  39. package/query/raw.js +1 -1
  40. package/schema/DatabaseSchema.js +21 -15
  41. package/schema/DatabaseTable.js +114 -54
  42. package/schema/SchemaComparator.js +56 -32
  43. package/schema/SchemaHelper.js +28 -8
  44. package/schema/SqlSchemaGenerator.d.ts +2 -1
  45. package/schema/SqlSchemaGenerator.js +15 -8
  46. package/tsconfig.build.tsbuildinfo +1 -1
  47. package/typings.d.ts +15 -4
@@ -1,6 +1,5 @@
1
1
  import { ALIAS_REPLACEMENT, ALIAS_REPLACEMENT_RE, ArrayType, inspect, isRaw, LockMode, OptimisticLockError, QueryOperator, QueryOrderNumeric, raw, Raw, QueryHelper, ReferenceKind, Utils, ValidationError, } from '@mikro-orm/core';
2
2
  import { JoinType, QueryType } from './enums.js';
3
- import { NativeQueryBuilder } from './NativeQueryBuilder.js';
4
3
  /**
5
4
  * @internal
6
5
  */
@@ -175,13 +174,25 @@ export class QueryBuilderHelper {
175
174
  }
176
175
  }
177
176
  return {
178
- prop, type, cond, ownerAlias, alias, table, schema,
179
- joinColumns, inverseJoinColumns, primaryKeys,
177
+ prop,
178
+ type,
179
+ cond,
180
+ ownerAlias,
181
+ alias,
182
+ table,
183
+ schema,
184
+ joinColumns,
185
+ inverseJoinColumns,
186
+ primaryKeys,
180
187
  };
181
188
  }
182
189
  joinManyToOneReference(prop, ownerAlias, alias, type, cond = {}, schema) {
183
190
  return {
184
- prop, type, cond, ownerAlias, alias,
191
+ prop,
192
+ type,
193
+ cond,
194
+ ownerAlias,
195
+ alias,
185
196
  table: this.getTableName(prop.targetMeta.class),
186
197
  schema: prop.targetMeta?.schema === '*' ? '*' : this.driver.getSchemaName(prop.targetMeta, { schema }),
187
198
  joinColumns: prop.referencedColumnNames,
@@ -194,7 +205,9 @@ export class QueryBuilderHelper {
194
205
  const pivotMeta = this.metadata.find(prop.pivotEntity);
195
206
  const ret = {
196
207
  [`${ownerAlias}.${prop.name}#${pivotAlias}`]: {
197
- prop, type, ownerAlias,
208
+ prop,
209
+ type,
210
+ ownerAlias,
198
211
  alias: pivotAlias,
199
212
  inverseAlias: alias,
200
213
  joinColumns: prop.joinColumns,
@@ -256,7 +269,9 @@ export class QueryBuilderHelper {
256
269
  conditions.push(`${left} = ${this.platform.quoteIdentifier(right)}`);
257
270
  });
258
271
  }
259
- if (join.prop.targetMeta?.root.inheritanceType === 'sti' && join.prop.targetMeta?.discriminatorValue && !join.path?.endsWith('[pivot]')) {
272
+ if (join.prop.targetMeta?.root.inheritanceType === 'sti' &&
273
+ join.prop.targetMeta?.discriminatorValue &&
274
+ !join.path?.endsWith('[pivot]')) {
260
275
  const typeProperty = join.prop.targetMeta.root.discriminatorColumn;
261
276
  const alias = join.inverseAlias ?? join.alias;
262
277
  join.cond[`${alias}.${typeProperty}`] = join.prop.targetMeta.discriminatorValue;
@@ -455,7 +470,7 @@ export class QueryBuilderHelper {
455
470
  // grouped condition for one field, e.g. `{ age: { $gte: 10, $lt: 50 } }`
456
471
  if (size > 1) {
457
472
  const subCondition = Object.entries(value).map(([subKey, subValue]) => {
458
- return ({ [key]: { [subKey]: subValue } });
473
+ return { [key]: { [subKey]: subValue } };
459
474
  });
460
475
  for (const sub of subCondition) {
461
476
  this.append(() => this._appendQueryCondition(type, sub, '$and'), parts, params);
@@ -521,8 +536,8 @@ export class QueryBuilderHelper {
521
536
  else if (['$in', '$nin'].includes(op) && Array.isArray(value[op]) && value[op].length === 0) {
522
537
  parts.push(`1 = ${op === '$in' ? 0 : 1}`);
523
538
  }
524
- else if (value[op] instanceof Raw || value[op] instanceof NativeQueryBuilder) {
525
- const query = value[op] instanceof NativeQueryBuilder ? value[op].toRaw() : value[op];
539
+ else if (value[op] instanceof Raw || typeof value[op]?.toRaw === 'function') {
540
+ const query = value[op] instanceof Raw ? value[op] : value[op].toRaw();
526
541
  const mappedKey = this.mapper(key, type, query, null);
527
542
  let sql = query.sql;
528
543
  if (['$in', '$nin'].includes(op)) {
@@ -549,7 +564,11 @@ export class QueryBuilderHelper {
549
564
  return `(${tmp.join(', ')})`;
550
565
  }
551
566
  if (prop?.customType instanceof ArrayType) {
552
- const item = prop.customType.convertToDatabaseValue(value, this.platform, { fromQuery: true, key, mode: 'query' });
567
+ const item = prop.customType.convertToDatabaseValue(value, this.platform, {
568
+ fromQuery: true,
569
+ key,
570
+ mode: 'query',
571
+ });
553
572
  params.push(item);
554
573
  }
555
574
  else {
@@ -629,11 +648,14 @@ export class QueryBuilderHelper {
629
648
  const noPrefix = (prop?.persist === false && !prop.formula && !prop.embedded) || Raw.isKnownFragment(f);
630
649
  const column = this.mapper(noPrefix ? field : `${alias}.${field}`, type, undefined, null);
631
650
  /* v8 ignore next */
632
- const rawColumn = typeof column === 'string' ? column.split('.').map(e => this.platform.quoteIdentifier(e)).join('.') : column;
651
+ const rawColumn = typeof column === 'string'
652
+ ? column
653
+ .split('.')
654
+ .map(e => this.platform.quoteIdentifier(e))
655
+ .join('.')
656
+ : column;
633
657
  const customOrder = prop?.customOrder;
634
- let colPart = customOrder
635
- ? this.platform.generateCustomOrder(rawColumn, customOrder)
636
- : rawColumn;
658
+ let colPart = customOrder ? this.platform.generateCustomOrder(rawColumn, customOrder) : rawColumn;
637
659
  if (isRaw(colPart)) {
638
660
  colPart = this.platform.formatQuery(colPart.sql, colPart.params);
639
661
  }
@@ -672,7 +694,9 @@ export class QueryBuilderHelper {
672
694
  const fields = returningProps.flatMap((prop) => {
673
695
  if (prop.hasConvertToJSValueSQL) {
674
696
  const aliased = this.platform.quoteIdentifier(prop.fieldNames[0]);
675
- const sql = prop.customType.convertToJSValueSQL(aliased, this.platform) + ' as ' + this.platform.quoteIdentifier(prop.fieldNames[0]);
697
+ const sql = prop.customType.convertToJSValueSQL(aliased, this.platform) +
698
+ ' as ' +
699
+ this.platform.quoteIdentifier(prop.fieldNames[0]);
676
700
  return [raw(sql)];
677
701
  }
678
702
  return prop.fieldNames;
@@ -770,7 +794,9 @@ export class QueryBuilderHelper {
770
794
  // skip nesting parens if the value is simple = scalar or object without operators or with only single key, being the operator
771
795
  const keys = Utils.getObjectQueryKeys(sub);
772
796
  const val = sub[keys[0]];
773
- const simple = !Utils.isPlainObject(val) || Utils.getObjectKeysSize(val) === 1 || Object.keys(val).every(k => !Utils.isOperator(k));
797
+ const simple = !Utils.isPlainObject(val) ||
798
+ Utils.getObjectKeysSize(val) === 1 ||
799
+ Object.keys(val).every(k => !Utils.isOperator(k));
774
800
  if (keys.length === 1 && simple) {
775
801
  this.append(() => this._appendQueryCondition(type, sub, operator), parts, params);
776
802
  continue;
@@ -804,7 +830,11 @@ export class QueryBuilderHelper {
804
830
  }
805
831
  getProperty(field, alias) {
806
832
  const entityName = this.aliasMap[alias]?.entityName || this.entityName;
807
- const meta = this.metadata.get(entityName);
833
+ const meta = this.metadata.find(entityName);
834
+ // raw table name (e.g. CTE) — no metadata available
835
+ if (!meta) {
836
+ return undefined;
837
+ }
808
838
  // check if `alias` is not matching an embedded property name instead of alias, e.g. `address.city`
809
839
  if (alias) {
810
840
  const prop = meta.properties[alias];
@@ -7,7 +7,8 @@ import { QueryBuilder } from './QueryBuilder.js';
7
7
  */
8
8
  export class ScalarCriteriaNode extends CriteriaNode {
9
9
  process(qb, options) {
10
- const matchPopulateJoins = options?.matchPopulateJoins || (this.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind));
10
+ const matchPopulateJoins = options?.matchPopulateJoins ||
11
+ (this.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind));
11
12
  const nestedAlias = qb.getAliasForJoinPath(this.getPath(options), { ...options, matchPopulateJoins });
12
13
  if (this.shouldJoin(qb, nestedAlias)) {
13
14
  const path = this.getPath();
@@ -22,7 +23,7 @@ export class ScalarCriteriaNode extends CriteriaNode {
22
23
  }
23
24
  }
24
25
  if (this.payload instanceof QueryBuilder) {
25
- return this.payload.getNativeQuery().toRaw();
26
+ return this.payload.toRaw();
26
27
  }
27
28
  if (this.payload && typeof this.payload === 'object') {
28
29
  const keys = Object.keys(this.payload).filter(key => ARRAY_OPERATORS.includes(key) && Array.isArray(this.payload[key]));
@@ -36,14 +37,20 @@ export class ScalarCriteriaNode extends CriteriaNode {
36
37
  return this.shouldJoin(qb, alias);
37
38
  }
38
39
  shouldJoin(qb, nestedAlias) {
39
- if (!this.parent || !this.prop || (nestedAlias && [QueryType.SELECT, QueryType.COUNT].includes(qb.type ?? QueryType.SELECT))) {
40
+ if (!this.parent ||
41
+ !this.prop ||
42
+ (nestedAlias && [QueryType.SELECT, QueryType.COUNT].includes(qb.type ?? QueryType.SELECT))) {
40
43
  return false;
41
44
  }
42
45
  switch (this.prop.kind) {
43
- case ReferenceKind.ONE_TO_MANY: return true;
44
- case ReferenceKind.MANY_TO_MANY: return true;
45
- case ReferenceKind.ONE_TO_ONE: return !this.prop.owner;
46
- default: return false; // SCALAR, MANY_TO_ONE
46
+ case ReferenceKind.ONE_TO_MANY:
47
+ return true;
48
+ case ReferenceKind.MANY_TO_MANY:
49
+ return true;
50
+ case ReferenceKind.ONE_TO_ONE:
51
+ return !this.prop.owner;
52
+ default:
53
+ return false; // SCALAR, MANY_TO_ONE
47
54
  }
48
55
  }
49
56
  }
package/query/raw.js CHANGED
@@ -1,4 +1,4 @@
1
- import { raw as raw_, Utils } from '@mikro-orm/core';
1
+ import { raw as raw_, Utils, } from '@mikro-orm/core';
2
2
  /**
3
3
  * Creates raw SQL query fragment that can be assigned to a property or part of a filter. This fragment is represented
4
4
  * by `RawQueryFragment` class instance that can be serialized to a string, so it can be used both as an object value
@@ -81,8 +81,11 @@ export class DatabaseSchema {
81
81
  const parts = config.get('migrations').tableName.split('.');
82
82
  const migrationsTableName = parts[1] ?? parts[0];
83
83
  const migrationsSchemaName = parts.length > 1 ? parts[0] : config.get('schema', platform.getDefaultSchemaName());
84
- const tables = allTables.filter(t => this.isTableNameAllowed(t.table_name, takeTables, skipTables) && (t.table_name !== migrationsTableName || (t.schema_name && t.schema_name !== migrationsSchemaName)));
85
- await platform.getSchemaHelper().loadInformationSchema(schema, connection, tables, schemas && schemas.length > 0 ? schemas : undefined);
84
+ const tables = allTables.filter(t => this.isTableNameAllowed(t.table_name, takeTables, skipTables) &&
85
+ (t.table_name !== migrationsTableName || (t.schema_name && t.schema_name !== migrationsSchemaName)));
86
+ await platform
87
+ .getSchemaHelper()
88
+ .loadInformationSchema(schema, connection, tables, schemas && schemas.length > 0 ? schemas : undefined);
86
89
  // Load views from database
87
90
  await platform.getSchemaHelper().loadViews(schema, connection);
88
91
  // Load materialized views (PostgreSQL only)
@@ -140,9 +143,7 @@ export class DatabaseSchema {
140
143
  table.comment = meta.comment;
141
144
  // For TPT child entities, only use ownProps (properties defined in this entity only)
142
145
  // For all other entities (including TPT root), use all props
143
- const propsToProcess = meta.inheritanceType === 'tpt' && meta.tptParent && meta.ownProps
144
- ? meta.ownProps
145
- : meta.props;
146
+ const propsToProcess = meta.inheritanceType === 'tpt' && meta.tptParent && meta.ownProps ? meta.ownProps : meta.props;
146
147
  for (const prop of propsToProcess) {
147
148
  if (!this.shouldHaveColumn(meta, prop, skipColumns)) {
148
149
  continue;
@@ -177,7 +178,9 @@ export class DatabaseSchema {
177
178
  table.addIndex(meta, { properties: pkPropsForIndex.map(prop => prop.name) }, 'primary');
178
179
  for (const check of meta.checks) {
179
180
  const columnName = check.property ? meta.properties[check.property].fieldNames[0] : undefined;
180
- const expression = isRaw(check.expression) ? platform.formatQuery(check.expression.sql, check.expression.params) : check.expression;
181
+ const expression = isRaw(check.expression)
182
+ ? platform.formatQuery(check.expression.sql, check.expression.params)
183
+ : check.expression;
181
184
  table.addCheck({
182
185
  name: check.name,
183
186
  expression,
@@ -229,7 +232,7 @@ export class DatabaseSchema {
229
232
  const pkColumnNames = meta.primaryKeys.flatMap(pk => meta.properties[pk].fieldNames);
230
233
  const parentPkColumnNames = parent.primaryKeys.flatMap(pk => parent.properties[pk].fieldNames);
231
234
  // Determine the parent table name with schema
232
- const parentSchema = parent.schema === '*' ? undefined : parent.schema ?? config.get('schema', platform.getDefaultSchemaName());
235
+ const parentSchema = parent.schema === '*' ? undefined : (parent.schema ?? config.get('schema', platform.getDefaultSchemaName()));
233
236
  const parentTableName = parentSchema ? `${parentSchema}.${parent.tableName}` : parent.tableName;
234
237
  // Create FK constraint name
235
238
  const constraintName = platform.getIndexName(table.name, pkColumnNames, 'foreign');
@@ -284,7 +287,8 @@ export class DatabaseSchema {
284
287
  if (rootProp.kind === ReferenceKind.EMBEDDED) {
285
288
  return prop === rootProp || !rootProp.object;
286
289
  }
287
- return [ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE].includes(prop.kind) || (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner);
290
+ return ([ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE].includes(prop.kind) ||
291
+ (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner));
288
292
  }
289
293
  toJSON() {
290
294
  const { platform, namespaces, ...rest } = this;
@@ -293,19 +297,21 @@ export class DatabaseSchema {
293
297
  prune(schema, wildcardSchemaTables) {
294
298
  const hasWildcardSchema = wildcardSchemaTables.length > 0;
295
299
  this.tables = this.tables.filter(table => {
296
- return (!schema && !hasWildcardSchema) // no schema specified and we don't have any multi-schema entity
297
- || table.schema === schema // specified schema matches the table's one
298
- || (!schema && !wildcardSchemaTables.includes(table.name)); // no schema specified and the table has fixed one provided
300
+ return ((!schema && !hasWildcardSchema) || // no schema specified and we don't have any multi-schema entity
301
+ table.schema === schema || // specified schema matches the table's one
302
+ (!schema && !wildcardSchemaTables.includes(table.name))); // no schema specified and the table has fixed one provided
299
303
  });
300
304
  this.views = this.views.filter(view => {
301
305
  /* v8 ignore next */
302
- return (!schema && !hasWildcardSchema)
303
- || view.schema === schema
304
- || (!schema && !wildcardSchemaTables.includes(view.name));
306
+ return ((!schema && !hasWildcardSchema) ||
307
+ view.schema === schema ||
308
+ (!schema && !wildcardSchemaTables.includes(view.name)));
305
309
  });
306
310
  // remove namespaces of ignored tables and views
307
311
  for (const ns of this.namespaces) {
308
- if (!this.tables.some(t => t.schema === ns) && !this.views.some(v => v.schema === ns) && !Object.values(this.nativeEnums).some(e => e.schema === ns)) {
312
+ if (!this.tables.some(t => t.schema === ns) &&
313
+ !this.views.some(v => v.schema === ns) &&
314
+ !Object.values(this.nativeEnums).some(e => e.schema === ns)) {
309
315
  this.namespaces.delete(ns);
310
316
  }
311
317
  }
@@ -80,10 +80,13 @@ export class DatabaseTable {
80
80
  this.columns[field] = {
81
81
  name: prop.fieldNames[idx],
82
82
  type: prop.columnTypes[idx],
83
- generated: prop.generated instanceof RawQueryFragment ? this.platform.formatQuery(prop.generated.sql, prop.generated.params) : prop.generated,
83
+ generated: prop.generated instanceof RawQueryFragment
84
+ ? this.platform.formatQuery(prop.generated.sql, prop.generated.params)
85
+ : prop.generated,
84
86
  mappedType,
85
87
  unsigned: prop.unsigned && this.platform.isNumericColumn(mappedType),
86
- autoincrement: prop.autoincrement ?? (primary && prop.kind === ReferenceKind.SCALAR && this.platform.isNumericColumn(mappedType)),
88
+ autoincrement: prop.autoincrement ??
89
+ (primary && prop.kind === ReferenceKind.SCALAR && this.platform.isNumericColumn(mappedType)),
87
90
  primary,
88
91
  nullable: this.columns[field]?.nullable ?? !!prop.nullable,
89
92
  nativeEnumName: prop.nativeEnumName,
@@ -105,7 +108,9 @@ export class DatabaseTable {
105
108
  });
106
109
  if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && !prop.polymorphic) {
107
110
  const constraintName = this.getIndexName(prop.foreignKeyName ?? true, prop.fieldNames, 'foreign');
108
- let schema = prop.targetMeta.root.schema === '*' ? this.schema : (prop.targetMeta.root.schema ?? config.get('schema', this.platform.getDefaultSchemaName()));
111
+ let schema = prop.targetMeta.root.schema === '*'
112
+ ? this.schema
113
+ : (prop.targetMeta.root.schema ?? config.get('schema', this.platform.getDefaultSchemaName()));
109
114
  if (prop.referencedTableName.includes('.')) {
110
115
  schema = undefined;
111
116
  }
@@ -154,19 +159,20 @@ export class DatabaseTable {
154
159
  return this.platform.getIndexName(this.name, columnNames, type);
155
160
  }
156
161
  getEntityDeclaration(namingStrategy, schemaHelper, scalarPropertiesForRelations) {
157
- const { fksOnColumnProps, fksOnStandaloneProps, columnFks, fkIndexes, nullableForeignKeys, skippedColumnNames, } = this.foreignKeysToProps(namingStrategy, scalarPropertiesForRelations);
162
+ const { fksOnColumnProps, fksOnStandaloneProps, columnFks, fkIndexes, nullableForeignKeys, skippedColumnNames } = this.foreignKeysToProps(namingStrategy, scalarPropertiesForRelations);
158
163
  const name = namingStrategy.getEntityName(this.name, this.schema);
159
164
  const schema = new EntitySchema({ name, collection: this.name, schema: this.schema, comment: this.comment });
160
165
  const compositeFkIndexes = {};
161
166
  const compositeFkUniques = {};
162
- const potentiallyUnmappedIndexes = this.indexes.filter(index => !index.primary // Skip primary index. Whether it's in use by scalar column or FK, it's already mapped.
163
- && (( // Non-trivial non-composite indexes will be declared at the entity's metadata, though later outputted in the property
164
- index.columnNames.length > 1 // All composite indexes are to be mapped to entity decorators or FK props.
165
- || skippedColumnNames.includes(index.columnNames[0]) // Non-composite indexes for skipped columns are to be mapped as entity decorators.
166
- || index.deferMode || index.expression || !(index.columnNames[0] in columnFks)) // Trivial non-composite indexes for scalar props are to be mapped to the column.
167
- )
167
+ const potentiallyUnmappedIndexes = this.indexes.filter(index => !index.primary && // Skip primary index. Whether it's in use by scalar column or FK, it's already mapped.
168
+ // Non-trivial non-composite indexes will be declared at the entity's metadata, though later outputted in the property
169
+ (index.columnNames.length > 1 || // All composite indexes are to be mapped to entity decorators or FK props.
170
+ skippedColumnNames.includes(index.columnNames[0]) || // Non-composite indexes for skipped columns are to be mapped as entity decorators.
171
+ index.deferMode ||
172
+ index.expression ||
173
+ !(index.columnNames[0] in columnFks)) && // Trivial non-composite indexes for scalar props are to be mapped to the column.
168
174
  // ignore indexes that don't have all column names (this can happen in sqlite where there is no way to infer this for expressions)
169
- && !(index.columnNames.some(col => !col) && !index.expression));
175
+ !(index.columnNames.some(col => !col) && !index.expression));
170
176
  // Helper to map column name to property name
171
177
  const columnToPropertyName = (colName) => this.getPropertyName(namingStrategy, colName);
172
178
  for (const index of potentiallyUnmappedIndexes) {
@@ -198,8 +204,13 @@ export class DatabaseTable {
198
204
  }
199
205
  }
200
206
  // An index is trivial if it has no special options that require entity-level declaration
201
- const hasAdvancedOptions = index.columns?.length || index.include?.length || index.fillFactor ||
202
- index.type || index.invisible || index.disabled || index.clustered;
207
+ const hasAdvancedOptions = index.columns?.length ||
208
+ index.include?.length ||
209
+ index.fillFactor ||
210
+ index.type ||
211
+ index.invisible ||
212
+ index.disabled ||
213
+ index.clustered;
203
214
  const isTrivial = !index.deferMode && !index.expression && !hasAdvancedOptions;
204
215
  if (isTrivial) {
205
216
  // Index is for FK. Map to the FK prop and move on.
@@ -213,7 +224,8 @@ export class DatabaseTable {
213
224
  }
214
225
  }
215
226
  }
216
- const properties = ret.properties ?? this.getIndexProperties(index, columnFks, fksOnColumnProps, fksOnStandaloneProps, namingStrategy);
227
+ const properties = ret.properties ??
228
+ this.getIndexProperties(index, columnFks, fksOnColumnProps, fksOnStandaloneProps, namingStrategy);
217
229
  // If there is a column that cannot be unambiguously mapped to a prop, render an expression.
218
230
  if (typeof properties === 'undefined') {
219
231
  ret.expression ??= schemaHelper.getCreateIndexSQL(this.name, index);
@@ -237,7 +249,7 @@ export class DatabaseTable {
237
249
  }
238
250
  schema.addIndex(ret);
239
251
  }
240
- const addedStandaloneFkPropsBasedOnColumn = new Set;
252
+ const addedStandaloneFkPropsBasedOnColumn = new Set();
241
253
  const nonSkippedColumns = this.getColumns().filter(column => !skippedColumnNames.includes(column.name));
242
254
  for (const column of nonSkippedColumns) {
243
255
  const columnName = column.name;
@@ -259,10 +271,10 @@ export class DatabaseTable {
259
271
  schema.addProperty(prop.name, prop.type, prop);
260
272
  }
261
273
  const meta = schema.init().meta;
262
- const oneToOneCandidateProperties = meta.relations
263
- .filter(prop => prop.primary && prop.kind === ReferenceKind.MANY_TO_ONE);
264
- if (oneToOneCandidateProperties.length === 1
265
- && oneToOneCandidateProperties[0].fieldNames.length === (new Set(meta.getPrimaryProps().flatMap(prop => prop.fieldNames))).size) {
274
+ const oneToOneCandidateProperties = meta.relations.filter(prop => prop.primary && prop.kind === ReferenceKind.MANY_TO_ONE);
275
+ if (oneToOneCandidateProperties.length === 1 &&
276
+ oneToOneCandidateProperties[0].fieldNames.length ===
277
+ new Set(meta.getPrimaryProps().flatMap(prop => prop.fieldNames)).size) {
266
278
  oneToOneCandidateProperties[0].kind = ReferenceKind.ONE_TO_ONE;
267
279
  }
268
280
  return meta;
@@ -277,7 +289,8 @@ export class DatabaseTable {
277
289
  const standaloneFksBasedOnColumnNames = new Map();
278
290
  for (const currentFk of fks) {
279
291
  const fkIndex = this.findFkIndex(currentFk);
280
- if (currentFk.columnNames.length === 1 && !fks.some(fk => fk !== currentFk && fk.columnNames.length === 1 && currentFk.columnNames[0] === fk.columnNames[0])) {
292
+ if (currentFk.columnNames.length === 1 &&
293
+ !fks.some(fk => fk !== currentFk && fk.columnNames.length === 1 && currentFk.columnNames[0] === fk.columnNames[0])) {
281
294
  // Non-composite FK is the only possible one for a column. Render the column with it.
282
295
  const columnName = currentFk.columnNames[0];
283
296
  columnFks[columnName] ??= [];
@@ -316,7 +329,10 @@ export class DatabaseTable {
316
329
  if (nullableColumnsInFk.length > 0) {
317
330
  nullableForeignKeys.add(currentFk);
318
331
  }
319
- if (specificColumnNames.length === 1 && ((nullableColumnsInFk.length === currentFk.columnNames.length || nullableColumnsInFk.length === 0) || (nullableColumnsInFk.length === 1 && nullableColumnsInFk[0] === specificColumnNames[0]))) {
332
+ if (specificColumnNames.length === 1 &&
333
+ (nullableColumnsInFk.length === currentFk.columnNames.length ||
334
+ nullableColumnsInFk.length === 0 ||
335
+ (nullableColumnsInFk.length === 1 && nullableColumnsInFk[0] === specificColumnNames[0]))) {
320
336
  // Composite FK has exactly one column which is not used in any other FK.
321
337
  // The FK also doesn't have a mix of nullable and non-nullable columns,
322
338
  // or its only nullable column is this very one.
@@ -391,14 +407,16 @@ export class DatabaseTable {
391
407
  // But also does not skip if the column is not nullable, and yet all involved FKs are nullable,
392
408
  // or if one or more FKs involved has multiple nullable columns.
393
409
  smart: (column) => {
394
- return columnsInFks.includes(column.name)
395
- && !fksOnColumnProps.has(column.name)
396
- && (column.nullable
410
+ return (columnsInFks.includes(column.name) &&
411
+ !fksOnColumnProps.has(column.name) &&
412
+ (column.nullable
397
413
  ? columnFks[column.name].some(fk => !fk.columnNames.some(fkColumnName => fkColumnName !== column.name && this.getColumn(fkColumnName)?.nullable))
398
- : columnFks[column.name].some(fk => !nullableForeignKeys.has(fk)));
414
+ : columnFks[column.name].some(fk => !nullableForeignKeys.has(fk))));
399
415
  },
400
416
  };
401
- const skippedColumnNames = this.getColumns().filter(skippingHandlers[scalarPropertiesForRelations]).map(column => column.name);
417
+ const skippedColumnNames = this.getColumns()
418
+ .filter(skippingHandlers[scalarPropertiesForRelations])
419
+ .map(column => column.name);
402
420
  // Check standalone FKs named after columns for potential conflicts among themselves.
403
421
  // This typically happens when two standalone FKs named after a column resolve to the same prop name
404
422
  // because the respective columns include the referenced table in the name.
@@ -439,7 +457,8 @@ export class DatabaseTable {
439
457
  findFkIndex(currentFk) {
440
458
  const fkColumnsLength = currentFk.columnNames.length;
441
459
  const possibleIndexes = this.indexes.filter(index => {
442
- return index.columnNames.length === fkColumnsLength && !currentFk.columnNames.some((columnName, i) => index.columnNames[i] !== columnName);
460
+ return (index.columnNames.length === fkColumnsLength &&
461
+ !currentFk.columnNames.some((columnName, i) => index.columnNames[i] !== columnName));
443
462
  });
444
463
  possibleIndexes.sort((a, b) => {
445
464
  if (a.primary !== b.primary) {
@@ -502,7 +521,8 @@ export class DatabaseTable {
502
521
  return Array.from(propBaseNames).map(baseName => this.getPropertyName(namingStrategy, baseName, fksOnColumnProps.get(baseName)));
503
522
  }
504
523
  getSafeBaseNameForFkProp(namingStrategy, currentFk, fks, columnName) {
505
- if (columnName && this.getPropertyName(namingStrategy, columnName, currentFk) !== this.getPropertyName(namingStrategy, columnName)) {
524
+ if (columnName &&
525
+ this.getPropertyName(namingStrategy, columnName, currentFk) !== this.getPropertyName(namingStrategy, columnName)) {
506
526
  // The eligible scalar column name is different from the name of the FK prop of the same column.
507
527
  // Both can be safely rendered.
508
528
  // Use the column name as a base for the FK prop.
@@ -538,7 +558,9 @@ export class DatabaseTable {
538
558
  */
539
559
  getShortestName(skipDefaultSchema = true) {
540
560
  const defaultSchema = this.platform.getDefaultSchemaName();
541
- if (!this.schema || this.name.startsWith(defaultSchema + '.') || (this.schema === defaultSchema && skipDefaultSchema)) {
561
+ if (!this.schema ||
562
+ this.name.startsWith(defaultSchema + '.') ||
563
+ (this.schema === defaultSchema && skipDefaultSchema)) {
542
564
  return this.name;
543
565
  }
544
566
  return `${this.schema}.${this.name}`;
@@ -569,7 +591,7 @@ export class DatabaseTable {
569
591
  }
570
592
  getForeignKeyDeclaration(fk, namingStrategy, schemaHelper, fkIndex, nullable, propNameBase, fksOnColumnProps) {
571
593
  const prop = this.getPropertyName(namingStrategy, propNameBase, fk);
572
- const kind = (fkIndex?.unique && !fkIndex.primary) ? this.getReferenceKind(fk, fkIndex) : this.getReferenceKind(fk);
594
+ const kind = fkIndex?.unique && !fkIndex.primary ? this.getReferenceKind(fk, fkIndex) : this.getReferenceKind(fk);
573
595
  const runtimeType = this.getPropertyTypeForForeignKey(namingStrategy, fk);
574
596
  const fkOptions = {};
575
597
  fkOptions.fieldNames = fk.columnNames;
@@ -584,8 +606,8 @@ export class DatabaseTable {
584
606
  const column = this.getColumn(fk.columnNames[0]);
585
607
  const defaultRaw = this.getPropertyDefaultValue(schemaHelper, column, column.type, true);
586
608
  const defaultTs = this.getPropertyDefaultValue(schemaHelper, column, column.type);
587
- columnOptions.default = (defaultRaw !== defaultTs || defaultRaw === '') ? defaultTs : undefined;
588
- columnOptions.defaultRaw = (column.nullable && defaultRaw === 'null') ? undefined : defaultRaw;
609
+ columnOptions.default = defaultRaw !== defaultTs || defaultRaw === '' ? defaultTs : undefined;
610
+ columnOptions.defaultRaw = column.nullable && defaultRaw === 'null' ? undefined : defaultRaw;
589
611
  columnOptions.optional = typeof column.generated !== 'undefined' || defaultRaw !== 'null';
590
612
  columnOptions.generated = column.generated;
591
613
  columnOptions.nullable = column.nullable;
@@ -607,25 +629,29 @@ export class DatabaseTable {
607
629
  nullable,
608
630
  primary: fkIndex?.primary || !fk.columnNames.some(columnName => !this.getPrimaryKey()?.columnNames.includes(columnName)),
609
631
  index: !fkIndex?.unique ? fkIndex?.keyName : undefined,
610
- unique: (fkIndex?.unique && !fkIndex.primary) ? fkIndex.keyName : undefined,
632
+ unique: fkIndex?.unique && !fkIndex.primary ? fkIndex.keyName : undefined,
611
633
  ...fkOptions,
612
634
  };
613
635
  }
614
636
  getPropertyDeclaration(column, namingStrategy, schemaHelper, compositeFkIndexes, compositeFkUniques, columnFks, fk) {
615
637
  const prop = this.getPropertyName(namingStrategy, column.name, fk);
616
638
  const persist = !(column.name in columnFks && typeof fk === 'undefined');
617
- const index = compositeFkIndexes[prop] || this.indexes.find(idx => idx.columnNames[0] === column.name && !idx.composite && !idx.unique && !idx.primary);
618
- const unique = compositeFkUniques[prop] || this.indexes.find(idx => idx.columnNames[0] === column.name && !idx.composite && idx.unique && !idx.primary);
639
+ const index = compositeFkIndexes[prop] ||
640
+ this.indexes.find(idx => idx.columnNames[0] === column.name && !idx.composite && !idx.unique && !idx.primary);
641
+ const unique = compositeFkUniques[prop] ||
642
+ this.indexes.find(idx => idx.columnNames[0] === column.name && !idx.composite && idx.unique && !idx.primary);
619
643
  const kind = this.getReferenceKind(fk, unique);
620
644
  const runtimeType = this.getPropertyTypeForColumn(namingStrategy, column, fk);
621
- const type = fk ? runtimeType : (Utils.keys(t).find(k => {
622
- const typeInCoreMap = this.platform.getMappedType(k);
623
- return (typeInCoreMap !== Type.getType(UnknownType) || k === 'unknown') && typeInCoreMap === column.mappedType;
624
- }) ?? runtimeType);
625
- const ignoreSchemaChanges = (type === 'unknown' && column.length) ? (column.extra ? ['type', 'extra'] : ['type']) : undefined;
645
+ const type = fk
646
+ ? runtimeType
647
+ : (Utils.keys(t).find(k => {
648
+ const typeInCoreMap = this.platform.getMappedType(k);
649
+ return ((typeInCoreMap !== Type.getType(UnknownType) || k === 'unknown') && typeInCoreMap === column.mappedType);
650
+ }) ?? runtimeType);
651
+ const ignoreSchemaChanges = type === 'unknown' && column.length ? (column.extra ? ['type', 'extra'] : ['type']) : undefined;
626
652
  const defaultRaw = this.getPropertyDefaultValue(schemaHelper, column, runtimeType, true);
627
653
  const defaultParsed = this.getPropertyDefaultValue(schemaHelper, column, runtimeType);
628
- const defaultTs = (defaultRaw !== defaultParsed || defaultParsed === '') ? defaultParsed : undefined;
654
+ const defaultTs = defaultRaw !== defaultParsed || defaultParsed === '' ? defaultParsed : undefined;
629
655
  const fkOptions = {};
630
656
  if (fk) {
631
657
  fkOptions.fieldNames = fk.columnNames;
@@ -646,7 +672,7 @@ export class DatabaseTable {
646
672
  optional: defaultRaw !== 'null' || defaultTs != null || typeof column.generated !== 'undefined',
647
673
  columnType: column.type,
648
674
  default: defaultTs,
649
- defaultRaw: (column.nullable && defaultRaw === 'null') ? undefined : defaultRaw,
675
+ defaultRaw: column.nullable && defaultRaw === 'null' ? undefined : defaultRaw,
650
676
  nullable: column.nullable,
651
677
  primary: column.primary && persist,
652
678
  autoincrement: column.autoincrement,
@@ -720,7 +746,7 @@ export class DatabaseTable {
720
746
  const defaultValue = column.default ?? 'null';
721
747
  const val = schemaHelper.normalizeDefaultValue(defaultValue, column.length);
722
748
  if (val === 'null') {
723
- return raw ? 'null' : (column.nullable ? null : undefined);
749
+ return raw ? 'null' : column.nullable ? null : undefined;
724
750
  }
725
751
  if (propType === 'boolean' && !raw) {
726
752
  return !['0', 'false', 'f', 'n', 'no', 'off'].includes('' + column.default);
@@ -752,7 +778,9 @@ export class DatabaseTable {
752
778
  }
753
779
  addIndex(meta, index, type) {
754
780
  // If columns are specified but properties are not, derive properties from column names
755
- if (index.columns?.length && !index.expression && (!index.properties || Utils.asArray(index.properties).length === 0)) {
781
+ if (index.columns?.length &&
782
+ !index.expression &&
783
+ (!index.properties || Utils.asArray(index.properties).length === 0)) {
756
784
  index = { ...index, properties: index.columns.map(c => c.name) };
757
785
  }
758
786
  const properties = Utils.unique(Utils.flatten(Utils.asArray(index.properties).map(prop => {
@@ -787,13 +815,15 @@ export class DatabaseTable {
787
815
  }
788
816
  const name = this.getIndexName(index.name, properties, type);
789
817
  // Process include columns (map property names to field names)
790
- const includeColumns = index.include ? Utils.unique(Utils.flatten(Utils.asArray(index.include).map(prop => {
791
- if (meta.properties[prop]) {
792
- return meta.properties[prop].fieldNames;
793
- }
794
- /* v8 ignore next */
795
- return [prop];
796
- }))) : undefined;
818
+ const includeColumns = index.include
819
+ ? Utils.unique(Utils.flatten(Utils.asArray(index.include).map(prop => {
820
+ if (meta.properties[prop]) {
821
+ return meta.properties[prop].fieldNames;
822
+ }
823
+ /* v8 ignore next */
824
+ return [prop];
825
+ })))
826
+ : undefined;
797
827
  // Process columns with advanced options (map property names to field names)
798
828
  const columns = index.columns?.map(col => {
799
829
  const fieldName = meta.properties[col.name]?.fieldNames[0] ?? col.name;
@@ -843,9 +873,39 @@ export class DatabaseTable {
843
873
  toJSON() {
844
874
  const { platform, columns, ...rest } = this;
845
875
  const columnsMapped = Utils.keys(columns).reduce((o, col) => {
846
- const { mappedType, ...restCol } = columns[col];
847
- o[col] = restCol;
848
- o[col].mappedType = Utils.keys(t).find(k => t[k] === mappedType.constructor);
876
+ const c = columns[col];
877
+ const normalized = {
878
+ name: c.name,
879
+ type: c.type,
880
+ unsigned: !!c.unsigned,
881
+ autoincrement: !!c.autoincrement,
882
+ primary: !!c.primary,
883
+ nullable: !!c.nullable,
884
+ unique: !!c.unique,
885
+ length: c.length ?? null,
886
+ precision: c.precision ?? null,
887
+ scale: c.scale ?? null,
888
+ default: c.default ?? null,
889
+ comment: c.comment ?? null,
890
+ enumItems: c.enumItems ?? [],
891
+ mappedType: Utils.keys(t).find(k => t[k] === c.mappedType.constructor),
892
+ };
893
+ if (c.generated) {
894
+ normalized.generated = c.generated;
895
+ }
896
+ if (c.nativeEnumName) {
897
+ normalized.nativeEnumName = c.nativeEnumName;
898
+ }
899
+ if (c.extra) {
900
+ normalized.extra = c.extra;
901
+ }
902
+ if (c.ignoreSchemaChanges) {
903
+ normalized.ignoreSchemaChanges = c.ignoreSchemaChanges;
904
+ }
905
+ if (c.defaultConstraint) {
906
+ normalized.defaultConstraint = c.defaultConstraint;
907
+ }
908
+ o[col] = normalized;
849
909
  return o;
850
910
  }, {});
851
911
  return { columns: columnsMapped, ...rest };