@mikro-orm/sql 7.0.0-rc.2 → 7.0.0

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 (66) hide show
  1. package/AbstractSqlConnection.d.ts +5 -4
  2. package/AbstractSqlConnection.js +20 -6
  3. package/AbstractSqlDriver.d.ts +19 -13
  4. package/AbstractSqlDriver.js +225 -47
  5. package/AbstractSqlPlatform.d.ts +35 -0
  6. package/AbstractSqlPlatform.js +51 -5
  7. package/PivotCollectionPersister.d.ts +2 -11
  8. package/PivotCollectionPersister.js +59 -59
  9. package/README.md +5 -4
  10. package/SqlEntityManager.d.ts +2 -2
  11. package/SqlEntityManager.js +5 -5
  12. package/dialects/index.d.ts +1 -0
  13. package/dialects/index.js +1 -0
  14. package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +2 -0
  15. package/dialects/mssql/MsSqlNativeQueryBuilder.js +8 -4
  16. package/dialects/mysql/BaseMySqlPlatform.d.ts +6 -0
  17. package/dialects/mysql/BaseMySqlPlatform.js +18 -2
  18. package/dialects/mysql/MySqlSchemaHelper.d.ts +1 -1
  19. package/dialects/mysql/MySqlSchemaHelper.js +25 -14
  20. package/dialects/oracledb/OracleDialect.d.ts +78 -0
  21. package/dialects/oracledb/OracleDialect.js +166 -0
  22. package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +19 -0
  23. package/dialects/oracledb/OracleNativeQueryBuilder.js +249 -0
  24. package/dialects/oracledb/index.d.ts +2 -0
  25. package/dialects/oracledb/index.js +2 -0
  26. package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +6 -0
  27. package/dialects/postgresql/BasePostgreSqlPlatform.js +49 -37
  28. package/dialects/postgresql/PostgreSqlSchemaHelper.js +75 -59
  29. package/dialects/sqlite/BaseSqliteConnection.js +2 -2
  30. package/dialects/sqlite/NodeSqliteDialect.js +3 -1
  31. package/dialects/sqlite/SqlitePlatform.d.ts +1 -0
  32. package/dialects/sqlite/SqlitePlatform.js +7 -1
  33. package/dialects/sqlite/SqliteSchemaHelper.js +23 -17
  34. package/index.d.ts +1 -1
  35. package/index.js +0 -1
  36. package/package.json +30 -30
  37. package/plugin/index.d.ts +1 -14
  38. package/plugin/index.js +13 -13
  39. package/plugin/transformer.d.ts +6 -22
  40. package/plugin/transformer.js +91 -82
  41. package/query/ArrayCriteriaNode.d.ts +1 -1
  42. package/query/CriteriaNode.js +28 -10
  43. package/query/CriteriaNodeFactory.js +20 -4
  44. package/query/NativeQueryBuilder.d.ts +28 -3
  45. package/query/NativeQueryBuilder.js +65 -3
  46. package/query/ObjectCriteriaNode.js +75 -31
  47. package/query/QueryBuilder.d.ts +199 -100
  48. package/query/QueryBuilder.js +544 -358
  49. package/query/QueryBuilderHelper.d.ts +18 -14
  50. package/query/QueryBuilderHelper.js +364 -147
  51. package/query/ScalarCriteriaNode.js +17 -8
  52. package/query/enums.d.ts +2 -0
  53. package/query/enums.js +2 -0
  54. package/query/raw.js +1 -1
  55. package/schema/DatabaseSchema.d.ts +7 -5
  56. package/schema/DatabaseSchema.js +68 -45
  57. package/schema/DatabaseTable.d.ts +8 -6
  58. package/schema/DatabaseTable.js +191 -107
  59. package/schema/SchemaComparator.d.ts +1 -3
  60. package/schema/SchemaComparator.js +76 -50
  61. package/schema/SchemaHelper.d.ts +2 -13
  62. package/schema/SchemaHelper.js +30 -9
  63. package/schema/SqlSchemaGenerator.d.ts +4 -14
  64. package/schema/SqlSchemaGenerator.js +26 -12
  65. package/typings.d.ts +10 -5
  66. package/tsconfig.build.tsbuildinfo +0 -1
@@ -3,51 +3,60 @@ import { DecimalType, EntitySchema, RawQueryFragment, ReferenceKind, t, Type, Un
3
3
  * @internal
4
4
  */
5
5
  export class DatabaseTable {
6
- platform;
7
6
  name;
8
7
  schema;
9
- columns = {};
10
- indexes = [];
11
- checks = [];
12
- foreignKeys = {};
8
+ #columns = {};
9
+ #indexes = [];
10
+ #checks = [];
11
+ #foreignKeys = {};
12
+ #platform;
13
13
  nativeEnums = {}; // for postgres
14
14
  comment;
15
15
  constructor(platform, name, schema) {
16
- this.platform = platform;
17
16
  this.name = name;
18
17
  this.schema = schema;
19
- Object.defineProperties(this, {
20
- platform: { enumerable: false, writable: true },
21
- });
18
+ this.#platform = platform;
22
19
  }
23
20
  getQuotedName() {
24
- return this.platform.quoteIdentifier(this.getShortestName());
21
+ return this.#platform.quoteIdentifier(this.getShortestName());
25
22
  }
26
23
  getColumns() {
27
- return Object.values(this.columns);
24
+ return Object.values(this.#columns);
28
25
  }
29
26
  getColumn(name) {
30
- return this.columns[name];
27
+ return this.#columns[name];
31
28
  }
32
29
  removeColumn(name) {
33
- delete this.columns[name];
30
+ delete this.#columns[name];
34
31
  }
35
32
  getIndexes() {
36
- return Utils.removeDuplicates(this.indexes);
33
+ return Utils.removeDuplicates(this.#indexes);
37
34
  }
38
35
  getChecks() {
39
- return this.checks;
36
+ return this.#checks;
37
+ }
38
+ /** @internal */
39
+ setIndexes(indexes) {
40
+ this.#indexes = indexes;
41
+ }
42
+ /** @internal */
43
+ setChecks(checks) {
44
+ this.#checks = checks;
45
+ }
46
+ /** @internal */
47
+ setForeignKeys(fks) {
48
+ this.#foreignKeys = fks;
40
49
  }
41
50
  init(cols, indexes = [], checks = [], pks, fks = {}, enums = {}) {
42
- this.indexes = indexes;
43
- this.checks = checks;
44
- this.foreignKeys = fks;
45
- this.columns = cols.reduce((o, v) => {
51
+ this.#indexes = indexes;
52
+ this.#checks = checks;
53
+ this.#foreignKeys = fks;
54
+ this.#columns = cols.reduce((o, v) => {
46
55
  const index = indexes.filter(i => i.columnNames[0] === v.name);
47
56
  v.primary = v.primary || pks.includes(v.name);
48
57
  v.unique = index.some(i => i.unique && !i.primary);
49
58
  const type = v.name in enums ? 'enum' : v.type;
50
- v.mappedType = this.platform.getMappedType(type);
59
+ v.mappedType = this.#platform.getMappedType(type);
51
60
  v.default = v.default?.toString().startsWith('nextval(') ? null : v.default;
52
61
  v.enumItems ??= enums[v.name] || [];
53
62
  o[v.name] = v;
@@ -55,14 +64,14 @@ export class DatabaseTable {
55
64
  }, {});
56
65
  }
57
66
  addColumn(column) {
58
- this.columns[column.name] = column;
67
+ this.#columns[column.name] = column;
59
68
  }
60
69
  addColumnFromProperty(prop, meta, config) {
61
70
  prop.fieldNames?.forEach((field, idx) => {
62
71
  const type = prop.enum ? 'enum' : prop.columnTypes[idx];
63
- const mappedType = this.platform.getMappedType(type);
72
+ const mappedType = this.#platform.getMappedType(type);
64
73
  if (mappedType instanceof DecimalType) {
65
- const match = prop.columnTypes[idx].match(/\w+\((\d+), ?(\d+)\)/);
74
+ const match = /\w+\((\d+), ?(\d+)\)/.exec(prop.columnTypes[idx]);
66
75
  /* v8 ignore next */
67
76
  if (match) {
68
77
  prop.precision ??= +match[1];
@@ -71,21 +80,24 @@ export class DatabaseTable {
71
80
  }
72
81
  }
73
82
  if (prop.length == null && prop.columnTypes[idx]) {
74
- prop.length = this.platform.getSchemaHelper().inferLengthFromColumnType(prop.columnTypes[idx]);
83
+ prop.length = this.#platform.getSchemaHelper().inferLengthFromColumnType(prop.columnTypes[idx]);
75
84
  if (typeof mappedType.getDefaultLength !== 'undefined') {
76
- prop.length ??= mappedType.getDefaultLength(this.platform);
85
+ prop.length ??= mappedType.getDefaultLength(this.#platform);
77
86
  }
78
87
  }
79
88
  const primary = !meta.compositePK && prop.fieldNames.length === 1 && !!prop.primary;
80
- this.columns[field] = {
89
+ this.#columns[field] = {
81
90
  name: prop.fieldNames[idx],
82
91
  type: prop.columnTypes[idx],
83
- generated: prop.generated instanceof RawQueryFragment ? this.platform.formatQuery(prop.generated.sql, prop.generated.params) : prop.generated,
92
+ generated: prop.generated instanceof RawQueryFragment
93
+ ? this.#platform.formatQuery(prop.generated.sql, prop.generated.params)
94
+ : prop.generated,
84
95
  mappedType,
85
- unsigned: prop.unsigned && this.platform.isNumericColumn(mappedType),
86
- autoincrement: prop.autoincrement ?? (primary && prop.kind === ReferenceKind.SCALAR && this.platform.isNumericColumn(mappedType)),
96
+ unsigned: prop.unsigned && this.#platform.isNumericColumn(mappedType),
97
+ autoincrement: prop.autoincrement ??
98
+ (primary && prop.kind === ReferenceKind.SCALAR && this.#platform.isNumericColumn(mappedType)),
87
99
  primary,
88
- nullable: this.columns[field]?.nullable ?? !!prop.nullable,
100
+ nullable: this.#columns[field]?.nullable ?? !!prop.nullable,
89
101
  nativeEnumName: prop.nativeEnumName,
90
102
  length: prop.length,
91
103
  precision: prop.precision,
@@ -96,21 +108,29 @@ export class DatabaseTable {
96
108
  extra: prop.extra,
97
109
  ignoreSchemaChanges: prop.ignoreSchemaChanges,
98
110
  };
99
- this.columns[field].unsigned ??= this.columns[field].autoincrement;
111
+ this.#columns[field].unsigned ??= this.#columns[field].autoincrement;
100
112
  if (this.nativeEnums[type]) {
101
- this.columns[field].enumItems ??= this.nativeEnums[type].items;
113
+ this.#columns[field].enumItems ??= this.nativeEnums[type].items;
102
114
  }
103
- const defaultValue = this.platform.getSchemaHelper().normalizeDefaultValue(prop.defaultRaw, prop.length);
104
- this.columns[field].default = defaultValue;
115
+ const defaultValue = this.#platform.getSchemaHelper().normalizeDefaultValue(prop.defaultRaw, prop.length);
116
+ this.#columns[field].default = defaultValue;
105
117
  });
106
118
  if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && !prop.polymorphic) {
107
119
  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()));
120
+ let schema = prop.targetMeta.root.schema === '*'
121
+ ? this.schema
122
+ : (prop.targetMeta.root.schema ?? config.get('schema', this.#platform.getDefaultSchemaName()));
109
123
  if (prop.referencedTableName.includes('.')) {
110
124
  schema = undefined;
111
125
  }
126
+ // For cross-schema FKs on MySQL/MariaDB (where schema = database), when the referenced
127
+ // table has no explicit schema but the current table does, qualify with dbName so the
128
+ // FK can resolve the referenced table in the correct database
129
+ if (!schema && this.schema && !this.#platform.getDefaultSchemaName()) {
130
+ schema = config.get('dbName');
131
+ }
112
132
  if (prop.createForeignKeyConstraint) {
113
- this.foreignKeys[constraintName] = {
133
+ this.#foreignKeys[constraintName] = {
114
134
  constraintName,
115
135
  columnNames: prop.fieldNames,
116
136
  localTableName: this.getShortestName(false),
@@ -118,15 +138,15 @@ export class DatabaseTable {
118
138
  referencedTableName: schema ? `${schema}.${prop.referencedTableName}` : prop.referencedTableName,
119
139
  };
120
140
  const schemaConfig = config.get('schemaGenerator');
121
- this.foreignKeys[constraintName].deleteRule = prop.deleteRule ?? schemaConfig.defaultDeleteRule;
122
- this.foreignKeys[constraintName].updateRule = prop.updateRule ?? schemaConfig.defaultUpdateRule;
141
+ this.#foreignKeys[constraintName].deleteRule = prop.deleteRule ?? schemaConfig.defaultDeleteRule;
142
+ this.#foreignKeys[constraintName].updateRule = prop.updateRule ?? schemaConfig.defaultUpdateRule;
123
143
  if (prop.deferMode) {
124
- this.foreignKeys[constraintName].deferMode = prop.deferMode;
144
+ this.#foreignKeys[constraintName].deferMode = prop.deferMode;
125
145
  }
126
146
  }
127
147
  }
128
148
  if (prop.index) {
129
- this.indexes.push({
149
+ this.#indexes.push({
130
150
  columnNames: prop.fieldNames,
131
151
  composite: prop.fieldNames.length > 1,
132
152
  keyName: this.getIndexName(prop.index, prop.fieldNames, 'index'),
@@ -136,7 +156,7 @@ export class DatabaseTable {
136
156
  });
137
157
  }
138
158
  if (prop.unique && !(prop.primary && !meta.compositePK)) {
139
- this.indexes.push({
159
+ this.#indexes.push({
140
160
  columnNames: prop.fieldNames,
141
161
  composite: prop.fieldNames.length > 1,
142
162
  keyName: this.getIndexName(prop.unique, prop.fieldNames, 'unique'),
@@ -151,22 +171,23 @@ export class DatabaseTable {
151
171
  if (typeof value === 'string') {
152
172
  return value;
153
173
  }
154
- return this.platform.getIndexName(this.name, columnNames, type);
174
+ return this.#platform.getIndexName(this.name, columnNames, type);
155
175
  }
156
176
  getEntityDeclaration(namingStrategy, schemaHelper, scalarPropertiesForRelations) {
157
- const { fksOnColumnProps, fksOnStandaloneProps, columnFks, fkIndexes, nullableForeignKeys, skippedColumnNames, } = this.foreignKeysToProps(namingStrategy, scalarPropertiesForRelations);
177
+ const { fksOnColumnProps, fksOnStandaloneProps, columnFks, fkIndexes, nullableForeignKeys, skippedColumnNames } = this.foreignKeysToProps(namingStrategy, scalarPropertiesForRelations);
158
178
  const name = namingStrategy.getEntityName(this.name, this.schema);
159
179
  const schema = new EntitySchema({ name, collection: this.name, schema: this.schema, comment: this.comment });
160
180
  const compositeFkIndexes = {};
161
181
  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
- )
182
+ 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.
183
+ // Non-trivial non-composite indexes will be declared at the entity's metadata, though later outputted in the property
184
+ (index.columnNames.length > 1 || // All composite indexes are to be mapped to entity decorators or FK props.
185
+ skippedColumnNames.includes(index.columnNames[0]) || // Non-composite indexes for skipped columns are to be mapped as entity decorators.
186
+ index.deferMode ||
187
+ index.expression ||
188
+ !(index.columnNames[0] in columnFks)) && // Trivial non-composite indexes for scalar props are to be mapped to the column.
168
189
  // 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));
190
+ !(index.columnNames.some(col => !col) && !index.expression));
170
191
  // Helper to map column name to property name
171
192
  const columnToPropertyName = (colName) => this.getPropertyName(namingStrategy, colName);
172
193
  for (const index of potentiallyUnmappedIndexes) {
@@ -198,8 +219,13 @@ export class DatabaseTable {
198
219
  }
199
220
  }
200
221
  // 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;
222
+ const hasAdvancedOptions = index.columns?.length ||
223
+ index.include?.length ||
224
+ index.fillFactor ||
225
+ index.type ||
226
+ index.invisible ||
227
+ index.disabled ||
228
+ index.clustered;
203
229
  const isTrivial = !index.deferMode && !index.expression && !hasAdvancedOptions;
204
230
  if (isTrivial) {
205
231
  // Index is for FK. Map to the FK prop and move on.
@@ -213,7 +239,8 @@ export class DatabaseTable {
213
239
  }
214
240
  }
215
241
  }
216
- const properties = ret.properties ?? this.getIndexProperties(index, columnFks, fksOnColumnProps, fksOnStandaloneProps, namingStrategy);
242
+ const properties = ret.properties ??
243
+ this.getIndexProperties(index, columnFks, fksOnColumnProps, fksOnStandaloneProps, namingStrategy);
217
244
  // If there is a column that cannot be unambiguously mapped to a prop, render an expression.
218
245
  if (typeof properties === 'undefined') {
219
246
  ret.expression ??= schemaHelper.getCreateIndexSQL(this.name, index);
@@ -237,7 +264,7 @@ export class DatabaseTable {
237
264
  }
238
265
  schema.addIndex(ret);
239
266
  }
240
- const addedStandaloneFkPropsBasedOnColumn = new Set;
267
+ const addedStandaloneFkPropsBasedOnColumn = new Set();
241
268
  const nonSkippedColumns = this.getColumns().filter(column => !skippedColumnNames.includes(column.name));
242
269
  for (const column of nonSkippedColumns) {
243
270
  const columnName = column.name;
@@ -259,10 +286,10 @@ export class DatabaseTable {
259
286
  schema.addProperty(prop.name, prop.type, prop);
260
287
  }
261
288
  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) {
289
+ const oneToOneCandidateProperties = meta.relations.filter(prop => prop.primary && prop.kind === ReferenceKind.MANY_TO_ONE);
290
+ if (oneToOneCandidateProperties.length === 1 &&
291
+ oneToOneCandidateProperties[0].fieldNames.length ===
292
+ new Set(meta.getPrimaryProps().flatMap(prop => prop.fieldNames)).size) {
266
293
  oneToOneCandidateProperties[0].kind = ReferenceKind.ONE_TO_ONE;
267
294
  }
268
295
  return meta;
@@ -277,7 +304,8 @@ export class DatabaseTable {
277
304
  const standaloneFksBasedOnColumnNames = new Map();
278
305
  for (const currentFk of fks) {
279
306
  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])) {
307
+ if (currentFk.columnNames.length === 1 &&
308
+ !fks.some(fk => fk !== currentFk && fk.columnNames.length === 1 && currentFk.columnNames[0] === fk.columnNames[0])) {
281
309
  // Non-composite FK is the only possible one for a column. Render the column with it.
282
310
  const columnName = currentFk.columnNames[0];
283
311
  columnFks[columnName] ??= [];
@@ -316,7 +344,10 @@ export class DatabaseTable {
316
344
  if (nullableColumnsInFk.length > 0) {
317
345
  nullableForeignKeys.add(currentFk);
318
346
  }
319
- if (specificColumnNames.length === 1 && ((nullableColumnsInFk.length === currentFk.columnNames.length || nullableColumnsInFk.length === 0) || (nullableColumnsInFk.length === 1 && nullableColumnsInFk[0] === specificColumnNames[0]))) {
347
+ if (specificColumnNames.length === 1 &&
348
+ (nullableColumnsInFk.length === currentFk.columnNames.length ||
349
+ nullableColumnsInFk.length === 0 ||
350
+ (nullableColumnsInFk.length === 1 && nullableColumnsInFk[0] === specificColumnNames[0]))) {
320
351
  // Composite FK has exactly one column which is not used in any other FK.
321
352
  // The FK also doesn't have a mix of nullable and non-nullable columns,
322
353
  // or its only nullable column is this very one.
@@ -391,14 +422,16 @@ export class DatabaseTable {
391
422
  // But also does not skip if the column is not nullable, and yet all involved FKs are nullable,
392
423
  // or if one or more FKs involved has multiple nullable columns.
393
424
  smart: (column) => {
394
- return columnsInFks.includes(column.name)
395
- && !fksOnColumnProps.has(column.name)
396
- && (column.nullable
425
+ return (columnsInFks.includes(column.name) &&
426
+ !fksOnColumnProps.has(column.name) &&
427
+ (column.nullable
397
428
  ? 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)));
429
+ : columnFks[column.name].some(fk => !nullableForeignKeys.has(fk))));
399
430
  },
400
431
  };
401
- const skippedColumnNames = this.getColumns().filter(skippingHandlers[scalarPropertiesForRelations]).map(column => column.name);
432
+ const skippedColumnNames = this.getColumns()
433
+ .filter(skippingHandlers[scalarPropertiesForRelations])
434
+ .map(column => column.name);
402
435
  // Check standalone FKs named after columns for potential conflicts among themselves.
403
436
  // This typically happens when two standalone FKs named after a column resolve to the same prop name
404
437
  // because the respective columns include the referenced table in the name.
@@ -438,8 +471,9 @@ export class DatabaseTable {
438
471
  }
439
472
  findFkIndex(currentFk) {
440
473
  const fkColumnsLength = currentFk.columnNames.length;
441
- const possibleIndexes = this.indexes.filter(index => {
442
- return index.columnNames.length === fkColumnsLength && !currentFk.columnNames.some((columnName, i) => index.columnNames[i] !== columnName);
474
+ const possibleIndexes = this.#indexes.filter(index => {
475
+ return (index.columnNames.length === fkColumnsLength &&
476
+ !currentFk.columnNames.some((columnName, i) => index.columnNames[i] !== columnName));
443
477
  });
444
478
  possibleIndexes.sort((a, b) => {
445
479
  if (a.primary !== b.primary) {
@@ -502,7 +536,8 @@ export class DatabaseTable {
502
536
  return Array.from(propBaseNames).map(baseName => this.getPropertyName(namingStrategy, baseName, fksOnColumnProps.get(baseName)));
503
537
  }
504
538
  getSafeBaseNameForFkProp(namingStrategy, currentFk, fks, columnName) {
505
- if (columnName && this.getPropertyName(namingStrategy, columnName, currentFk) !== this.getPropertyName(namingStrategy, columnName)) {
539
+ if (columnName &&
540
+ this.getPropertyName(namingStrategy, columnName, currentFk) !== this.getPropertyName(namingStrategy, columnName)) {
506
541
  // The eligible scalar column name is different from the name of the FK prop of the same column.
507
542
  // Both can be safely rendered.
508
543
  // Use the column name as a base for the FK prop.
@@ -537,39 +572,41 @@ export class DatabaseTable {
537
572
  * The shortest name is stripped of the default namespace. All other namespaced elements are returned as full-qualified names.
538
573
  */
539
574
  getShortestName(skipDefaultSchema = true) {
540
- const defaultSchema = this.platform.getDefaultSchemaName();
541
- if (!this.schema || this.name.startsWith(defaultSchema + '.') || (this.schema === defaultSchema && skipDefaultSchema)) {
575
+ const defaultSchema = this.#platform.getDefaultSchemaName();
576
+ if (!this.schema ||
577
+ this.name.startsWith(defaultSchema + '.') ||
578
+ (this.schema === defaultSchema && skipDefaultSchema)) {
542
579
  return this.name;
543
580
  }
544
581
  return `${this.schema}.${this.name}`;
545
582
  }
546
583
  getForeignKeys() {
547
- return this.foreignKeys;
584
+ return this.#foreignKeys;
548
585
  }
549
586
  hasColumn(columnName) {
550
- return columnName in this.columns;
587
+ return columnName in this.#columns;
551
588
  }
552
589
  getIndex(indexName) {
553
- return this.indexes.find(i => i.keyName === indexName);
590
+ return this.#indexes.find(i => i.keyName === indexName);
554
591
  }
555
592
  hasIndex(indexName) {
556
593
  return !!this.getIndex(indexName);
557
594
  }
558
595
  getCheck(checkName) {
559
- return this.checks.find(i => i.name === checkName);
596
+ return this.#checks.find(i => i.name === checkName);
560
597
  }
561
598
  hasCheck(checkName) {
562
599
  return !!this.getCheck(checkName);
563
600
  }
564
601
  getPrimaryKey() {
565
- return this.indexes.find(i => i.primary);
602
+ return this.#indexes.find(i => i.primary);
566
603
  }
567
604
  hasPrimaryKey() {
568
605
  return !!this.getPrimaryKey();
569
606
  }
570
607
  getForeignKeyDeclaration(fk, namingStrategy, schemaHelper, fkIndex, nullable, propNameBase, fksOnColumnProps) {
571
608
  const prop = this.getPropertyName(namingStrategy, propNameBase, fk);
572
- const kind = (fkIndex?.unique && !fkIndex.primary) ? this.getReferenceKind(fk, fkIndex) : this.getReferenceKind(fk);
609
+ const kind = fkIndex?.unique && !fkIndex.primary ? this.getReferenceKind(fk, fkIndex) : this.getReferenceKind(fk);
573
610
  const runtimeType = this.getPropertyTypeForForeignKey(namingStrategy, fk);
574
611
  const fkOptions = {};
575
612
  fkOptions.fieldNames = fk.columnNames;
@@ -584,8 +621,8 @@ export class DatabaseTable {
584
621
  const column = this.getColumn(fk.columnNames[0]);
585
622
  const defaultRaw = this.getPropertyDefaultValue(schemaHelper, column, column.type, true);
586
623
  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;
624
+ columnOptions.default = defaultRaw !== defaultTs || defaultRaw === '' ? defaultTs : undefined;
625
+ columnOptions.defaultRaw = column.nullable && defaultRaw === 'null' ? undefined : defaultRaw;
589
626
  columnOptions.optional = typeof column.generated !== 'undefined' || defaultRaw !== 'null';
590
627
  columnOptions.generated = column.generated;
591
628
  columnOptions.nullable = column.nullable;
@@ -607,25 +644,29 @@ export class DatabaseTable {
607
644
  nullable,
608
645
  primary: fkIndex?.primary || !fk.columnNames.some(columnName => !this.getPrimaryKey()?.columnNames.includes(columnName)),
609
646
  index: !fkIndex?.unique ? fkIndex?.keyName : undefined,
610
- unique: (fkIndex?.unique && !fkIndex.primary) ? fkIndex.keyName : undefined,
647
+ unique: fkIndex?.unique && !fkIndex.primary ? fkIndex.keyName : undefined,
611
648
  ...fkOptions,
612
649
  };
613
650
  }
614
651
  getPropertyDeclaration(column, namingStrategy, schemaHelper, compositeFkIndexes, compositeFkUniques, columnFks, fk) {
615
652
  const prop = this.getPropertyName(namingStrategy, column.name, fk);
616
653
  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);
654
+ const index = compositeFkIndexes[prop] ||
655
+ this.#indexes.find(idx => idx.columnNames[0] === column.name && !idx.composite && !idx.unique && !idx.primary);
656
+ const unique = compositeFkUniques[prop] ||
657
+ this.#indexes.find(idx => idx.columnNames[0] === column.name && !idx.composite && idx.unique && !idx.primary);
619
658
  const kind = this.getReferenceKind(fk, unique);
620
659
  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;
660
+ const type = fk
661
+ ? runtimeType
662
+ : (Utils.keys(t).find(k => {
663
+ const typeInCoreMap = this.#platform.getMappedType(k);
664
+ return ((typeInCoreMap !== Type.getType(UnknownType) || k === 'unknown') && typeInCoreMap === column.mappedType);
665
+ }) ?? runtimeType);
666
+ const ignoreSchemaChanges = type === 'unknown' && column.length ? (column.extra ? ['type', 'extra'] : ['type']) : undefined;
626
667
  const defaultRaw = this.getPropertyDefaultValue(schemaHelper, column, runtimeType, true);
627
668
  const defaultParsed = this.getPropertyDefaultValue(schemaHelper, column, runtimeType);
628
- const defaultTs = (defaultRaw !== defaultParsed || defaultParsed === '') ? defaultParsed : undefined;
669
+ const defaultTs = defaultRaw !== defaultParsed || defaultParsed === '' ? defaultParsed : undefined;
629
670
  const fkOptions = {};
630
671
  if (fk) {
631
672
  fkOptions.fieldNames = fk.columnNames;
@@ -646,7 +687,7 @@ export class DatabaseTable {
646
687
  optional: defaultRaw !== 'null' || defaultTs != null || typeof column.generated !== 'undefined',
647
688
  columnType: column.type,
648
689
  default: defaultTs,
649
- defaultRaw: (column.nullable && defaultRaw === 'null') ? undefined : defaultRaw,
690
+ defaultRaw: column.nullable && defaultRaw === 'null' ? undefined : defaultRaw,
650
691
  nullable: column.nullable,
651
692
  primary: column.primary && persist,
652
693
  autoincrement: column.autoincrement,
@@ -702,7 +743,7 @@ export class DatabaseTable {
702
743
  if (fk) {
703
744
  return this.getPropertyTypeForForeignKey(namingStrategy, fk);
704
745
  }
705
- const enumMode = this.platform.getConfig().get('entityGenerator').enumMode;
746
+ const enumMode = this.#platform.getConfig().get('entityGenerator').enumMode;
706
747
  // If this column is using an enum.
707
748
  if (column.enumItems?.length) {
708
749
  const name = column.nativeEnumName ?? column.name;
@@ -720,7 +761,7 @@ export class DatabaseTable {
720
761
  const defaultValue = column.default ?? 'null';
721
762
  const val = schemaHelper.normalizeDefaultValue(defaultValue, column.length);
722
763
  if (val === 'null') {
723
- return raw ? 'null' : (column.nullable ? null : undefined);
764
+ return raw ? 'null' : column.nullable ? null : undefined;
724
765
  }
725
766
  if (propType === 'boolean' && !raw) {
726
767
  return !['0', 'false', 'f', 'n', 'no', 'off'].includes('' + column.default);
@@ -729,7 +770,7 @@ export class DatabaseTable {
729
770
  return +defaultValue;
730
771
  }
731
772
  // unquote string defaults if `raw = false`
732
- const match = ('' + val).match(/^'(.*)'$/);
773
+ const match = /^'(.*)'$/.exec('' + val);
733
774
  if (!raw && match) {
734
775
  return match[1];
735
776
  }
@@ -746,13 +787,15 @@ export class DatabaseTable {
746
787
  };
747
788
  const columns = meta.createSchemaColumnMappingObject();
748
789
  const exp = expression(columns, table, indexName);
749
- return exp instanceof RawQueryFragment ? this.platform.formatQuery(exp.sql, exp.params) : exp;
790
+ return exp instanceof RawQueryFragment ? this.#platform.formatQuery(exp.sql, exp.params) : exp;
750
791
  }
751
792
  return expression;
752
793
  }
753
794
  addIndex(meta, index, type) {
754
795
  // 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)) {
796
+ if (index.columns?.length &&
797
+ !index.expression &&
798
+ (!index.properties || Utils.asArray(index.properties).length === 0)) {
756
799
  index = { ...index, properties: index.columns.map(c => c.name) };
757
800
  }
758
801
  const properties = Utils.unique(Utils.flatten(Utils.asArray(index.properties).map(prop => {
@@ -787,13 +830,15 @@ export class DatabaseTable {
787
830
  }
788
831
  const name = this.getIndexName(index.name, properties, type);
789
832
  // 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;
833
+ const includeColumns = index.include
834
+ ? Utils.unique(Utils.flatten(Utils.asArray(index.include).map(prop => {
835
+ if (meta.properties[prop]) {
836
+ return meta.properties[prop].fieldNames;
837
+ }
838
+ /* v8 ignore next */
839
+ return [prop];
840
+ })))
841
+ : undefined;
797
842
  // Process columns with advanced options (map property names to field names)
798
843
  const columns = index.columns?.map(col => {
799
844
  const fieldName = meta.properties[col.name]?.fieldNames[0] ?? col.name;
@@ -817,7 +862,7 @@ export class DatabaseTable {
817
862
  if (index.fillFactor != null && (index.fillFactor < 0 || index.fillFactor > 100)) {
818
863
  throw new Error(`fillFactor must be between 0 and 100, got ${index.fillFactor} for index '${name}' on entity '${meta.className}'`);
819
864
  }
820
- this.indexes.push({
865
+ this.#indexes.push({
821
866
  keyName: name,
822
867
  columnNames: properties,
823
868
  composite: properties.length > 1,
@@ -838,16 +883,55 @@ export class DatabaseTable {
838
883
  });
839
884
  }
840
885
  addCheck(check) {
841
- this.checks.push(check);
886
+ this.#checks.push(check);
842
887
  }
843
888
  toJSON() {
844
- const { platform, columns, ...rest } = this;
889
+ const columns = this.#columns;
845
890
  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);
891
+ const c = columns[col];
892
+ const normalized = {
893
+ name: c.name,
894
+ type: c.type,
895
+ unsigned: !!c.unsigned,
896
+ autoincrement: !!c.autoincrement,
897
+ primary: !!c.primary,
898
+ nullable: !!c.nullable,
899
+ unique: !!c.unique,
900
+ length: c.length ?? null,
901
+ precision: c.precision ?? null,
902
+ scale: c.scale ?? null,
903
+ default: c.default ?? null,
904
+ comment: c.comment ?? null,
905
+ enumItems: c.enumItems ?? [],
906
+ mappedType: Utils.keys(t).find(k => t[k] === c.mappedType.constructor),
907
+ };
908
+ if (c.generated) {
909
+ normalized.generated = c.generated;
910
+ }
911
+ if (c.nativeEnumName) {
912
+ normalized.nativeEnumName = c.nativeEnumName;
913
+ }
914
+ if (c.extra) {
915
+ normalized.extra = c.extra;
916
+ }
917
+ if (c.ignoreSchemaChanges) {
918
+ normalized.ignoreSchemaChanges = c.ignoreSchemaChanges;
919
+ }
920
+ if (c.defaultConstraint) {
921
+ normalized.defaultConstraint = c.defaultConstraint;
922
+ }
923
+ o[col] = normalized;
849
924
  return o;
850
925
  }, {});
851
- return { columns: columnsMapped, ...rest };
926
+ return {
927
+ name: this.name,
928
+ schema: this.schema,
929
+ columns: columnsMapped,
930
+ indexes: this.#indexes,
931
+ checks: this.#checks,
932
+ foreignKeys: this.#foreignKeys,
933
+ nativeEnums: this.nativeEnums,
934
+ comment: this.comment,
935
+ };
852
936
  }
853
937
  }
@@ -7,9 +7,7 @@ import type { AbstractSqlPlatform } from '../AbstractSqlPlatform.js';
7
7
  * Compares two Schemas and return an instance of SchemaDifference.
8
8
  */
9
9
  export declare class SchemaComparator {
10
- private readonly platform;
11
- private readonly helper;
12
- private readonly logger;
10
+ #private;
13
11
  constructor(platform: AbstractSqlPlatform);
14
12
  /**
15
13
  * Returns a SchemaDifference object containing the differences between the schemas fromSchema and toSchema.