@mikro-orm/knex 6.3.5-dev.5 → 6.3.5-dev.7

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.
@@ -92,7 +92,7 @@ class MySqlSchemaHelper extends SchemaHelper_1.SchemaHelper {
92
92
  order by ordinal_position`;
93
93
  const allColumns = await connection.execute(sql);
94
94
  const str = (val) => val != null ? '' + val : val;
95
- const extra = (val) => val.replace(/auto_increment|default_generated|(stored|virtual) generated/i, '').trim();
95
+ const extra = (val) => val.replace(/auto_increment|default_generated|(stored|virtual) generated/i, '').trim() || undefined;
96
96
  const ret = {};
97
97
  for (const col of allColumns) {
98
98
  const mappedType = this.platform.getMappedType(col.column_type);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/knex",
3
- "version": "6.3.5-dev.5",
3
+ "version": "6.3.5-dev.7",
4
4
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",
@@ -66,7 +66,7 @@
66
66
  "@mikro-orm/core": "^6.3.4"
67
67
  },
68
68
  "peerDependencies": {
69
- "@mikro-orm/core": "6.3.5-dev.5",
69
+ "@mikro-orm/core": "6.3.5-dev.7",
70
70
  "better-sqlite3": "*",
71
71
  "libsql": "*",
72
72
  "mariadb": "*"
@@ -170,36 +170,46 @@ class DatabaseTable {
170
170
  const compositeFkUniques = {};
171
171
  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.
172
172
  && (index.columnNames.length > 1 // All composite indexes are to be mapped to entity decorators or FK props.
173
- || !(index.columnNames[0] in columnFks) // Non-composite indexes for scalar props are to be mapped to the column.
174
173
  || skippedColumnNames.includes(index.columnNames[0]) // Non-composite indexes for skipped columns are to be mapped as entity decorators.
174
+ || index.deferMode || index.expression // Non-trivial non-composite indexes will be declared at the entity's metadata, though later outputted in the property
175
+ || !(index.columnNames[0] in columnFks) // Trivial non-composite indexes for scalar props are to be mapped to the column.
175
176
  )
176
177
  // ignore indexes that don't have all column names (this can happen in sqlite where there is no way to infer this for expressions)
177
178
  && !(index.columnNames.some(col => !col) && !index.expression));
178
179
  for (const index of potentiallyUnmappedIndexes) {
179
- const ret = { name: index.keyName };
180
- // Index is for FK. Map to the FK prop and move on.
181
- const fkForIndex = fkIndexes.get(index);
182
- if (fkForIndex && !fkForIndex.fk.columnNames.some(col => !index.columnNames.includes(col))) {
183
- ret.properties = [this.getPropertyName(namingStrategy, fkForIndex.baseName, fkForIndex.fk)];
184
- const map = index.unique ? compositeFkUniques : compositeFkIndexes;
185
- map[ret.properties[0]] = { keyName: index.keyName };
186
- continue;
180
+ const ret = {
181
+ name: index.keyName,
182
+ deferMode: index.deferMode,
183
+ expression: index.expression,
184
+ };
185
+ const isTrivial = !index.deferMode && !index.expression;
186
+ if (isTrivial) {
187
+ // Index is for FK. Map to the FK prop and move on.
188
+ const fkForIndex = fkIndexes.get(index);
189
+ if (fkForIndex && !fkForIndex.fk.columnNames.some(col => !index.columnNames.includes(col))) {
190
+ ret.properties = [this.getPropertyName(namingStrategy, fkForIndex.baseName, fkForIndex.fk)];
191
+ const map = index.unique ? compositeFkUniques : compositeFkIndexes;
192
+ if (typeof map[ret.properties[0]] === 'undefined') {
193
+ map[ret.properties[0]] = index;
194
+ continue;
195
+ }
196
+ }
187
197
  }
188
- const properties = this.getIndexProperties(index, columnFks, fksOnColumnProps, fksOnStandaloneProps, namingStrategy);
198
+ const properties = ret.properties ?? this.getIndexProperties(index, columnFks, fksOnColumnProps, fksOnStandaloneProps, namingStrategy);
189
199
  // If there is a column that cannot be unambiguously mapped to a prop, render an expression.
190
- if (index.expression) {
191
- ret.expression = index.expression;
192
- }
193
- else if (typeof properties === 'undefined') {
194
- ret.expression = schemaHelper.getCreateIndexSQL(this.name, index);
200
+ if (typeof properties === 'undefined') {
201
+ ret.expression ??= schemaHelper.getCreateIndexSQL(this.name, index);
195
202
  }
196
203
  else {
197
- ret.properties = properties;
204
+ ret.properties ??= properties;
198
205
  // If the index is for one property that is not a FK prop, map to the column prop and move on.
199
- if (properties.length === 1 && !fksOnStandaloneProps.has(properties[0])) {
206
+ if (properties.length === 1 && isTrivial && !fksOnStandaloneProps.has(properties[0])) {
200
207
  const map = index.unique ? compositeFkUniques : compositeFkIndexes;
201
- map[properties[0]] = { keyName: index.keyName };
202
- continue;
208
+ // Only map one trivial index. If the same column is indexed many times over, output
209
+ if (typeof map[properties[0]] === 'undefined') {
210
+ map[properties[0]] = index;
211
+ continue;
212
+ }
203
213
  }
204
214
  }
205
215
  // Composite indexes that aren't exclusively mapped to FK props get an entity decorator.
@@ -216,18 +226,18 @@ class DatabaseTable {
216
226
  const standaloneFkPropBasedOnColumn = fksOnStandaloneProps.get(columnName);
217
227
  if (standaloneFkPropBasedOnColumn && !fksOnColumnProps.get(columnName)) {
218
228
  addedStandaloneFkPropsBasedOnColumn.add(columnName);
219
- const [fkIndex, currentFk] = standaloneFkPropBasedOnColumn;
220
- const prop = this.getForeignKeyDeclaration(currentFk, namingStrategy, schemaHelper, fkIndex, nullableForeignKeys.has(currentFk), columnName);
229
+ const { fkIndex, currentFk } = standaloneFkPropBasedOnColumn;
230
+ const prop = this.getForeignKeyDeclaration(currentFk, namingStrategy, schemaHelper, fkIndex, nullableForeignKeys.has(currentFk), columnName, fksOnColumnProps);
221
231
  schema.addProperty(prop.name, prop.type, prop);
222
232
  }
223
233
  const prop = this.getPropertyDeclaration(column, namingStrategy, schemaHelper, compositeFkIndexes, compositeFkUniques, columnFks, fksOnColumnProps.get(columnName));
224
234
  schema.addProperty(prop.name, prop.type, prop);
225
235
  }
226
- for (const [propBaseName, [fkIndex, currentFk]] of fksOnStandaloneProps.entries()) {
236
+ for (const [propBaseName, { fkIndex, currentFk }] of fksOnStandaloneProps.entries()) {
227
237
  if (addedStandaloneFkPropsBasedOnColumn.has(propBaseName)) {
228
238
  continue;
229
239
  }
230
- const prop = this.getForeignKeyDeclaration(currentFk, namingStrategy, schemaHelper, fkIndex, nullableForeignKeys.has(currentFk), propBaseName);
240
+ const prop = this.getForeignKeyDeclaration(currentFk, namingStrategy, schemaHelper, fkIndex, nullableForeignKeys.has(currentFk), propBaseName, fksOnColumnProps);
231
241
  schema.addProperty(prop.name, prop.type, prop);
232
242
  }
233
243
  const meta = schema.init().meta;
@@ -246,6 +256,7 @@ class DatabaseTable {
246
256
  const columnFks = {};
247
257
  const fkIndexes = new Map();
248
258
  const nullableForeignKeys = new Set();
259
+ const standaloneFksBasedOnColumnNames = new Map();
249
260
  for (const currentFk of fks) {
250
261
  const fkIndex = this.findFkIndex(currentFk);
251
262
  if (currentFk.columnNames.length === 1 && !fks.some(fk => fk !== currentFk && fk.columnNames.length === 1 && currentFk.columnNames[0] === fk.columnNames[0])) {
@@ -258,12 +269,17 @@ class DatabaseTable {
258
269
  }
259
270
  if (scalarPropertiesForRelations === 'always') {
260
271
  const baseName = this.getSafeBaseNameForFkProp(namingStrategy, currentFk, fks, columnName);
261
- fksOnStandaloneProps.set(baseName, [fkIndex, currentFk]);
262
- fkIndexes.set(fkIndex, { fk: currentFk, baseName });
272
+ standaloneFksBasedOnColumnNames.set(baseName, currentFk);
273
+ fksOnStandaloneProps.set(baseName, { fkIndex, currentFk });
274
+ if (fkIndex) {
275
+ fkIndexes.set(fkIndex, { fk: currentFk, baseName });
276
+ }
263
277
  }
264
278
  else {
265
279
  fksOnColumnProps.set(columnName, currentFk);
266
- fkIndexes.set(fkIndex, { fk: currentFk, baseName: columnName });
280
+ if (fkIndex) {
281
+ fkIndexes.set(fkIndex, { fk: currentFk, baseName: columnName });
282
+ }
267
283
  }
268
284
  continue;
269
285
  }
@@ -290,12 +306,17 @@ class DatabaseTable {
290
306
  const columnName = specificColumnNames[0];
291
307
  if (scalarPropertiesForRelations === 'always') {
292
308
  const baseName = this.getSafeBaseNameForFkProp(namingStrategy, currentFk, fks, columnName);
293
- fksOnStandaloneProps.set(baseName, [fkIndex, currentFk]);
294
- fkIndexes.set(fkIndex, { fk: currentFk, baseName });
309
+ standaloneFksBasedOnColumnNames.set(baseName, currentFk);
310
+ fksOnStandaloneProps.set(baseName, { fkIndex, currentFk });
311
+ if (fkIndex) {
312
+ fkIndexes.set(fkIndex, { fk: currentFk, baseName });
313
+ }
295
314
  }
296
315
  else {
297
316
  fksOnColumnProps.set(columnName, currentFk);
298
- fkIndexes.set(fkIndex, { fk: currentFk, baseName: columnName });
317
+ if (fkIndex) {
318
+ fkIndexes.set(fkIndex, { fk: currentFk, baseName: columnName });
319
+ }
299
320
  }
300
321
  continue;
301
322
  }
@@ -307,12 +328,17 @@ class DatabaseTable {
307
328
  const columnName = nullableColumnsInFk.at(0) ?? currentFk.columnNames[0];
308
329
  if (scalarPropertiesForRelations === 'always') {
309
330
  const baseName = this.getSafeBaseNameForFkProp(namingStrategy, currentFk, fks, columnName);
310
- fksOnStandaloneProps.set(baseName, [fkIndex, currentFk]);
311
- fkIndexes.set(fkIndex, { fk: currentFk, baseName });
331
+ standaloneFksBasedOnColumnNames.set(baseName, currentFk);
332
+ fksOnStandaloneProps.set(baseName, { fkIndex, currentFk });
333
+ if (fkIndex) {
334
+ fkIndexes.set(fkIndex, { fk: currentFk, baseName });
335
+ }
312
336
  }
313
337
  else {
314
338
  fksOnColumnProps.set(columnName, currentFk);
315
- fkIndexes.set(fkIndex, { fk: currentFk, baseName: columnName });
339
+ if (fkIndex) {
340
+ fkIndexes.set(fkIndex, { fk: currentFk, baseName: columnName });
341
+ }
316
342
  }
317
343
  continue;
318
344
  }
@@ -320,14 +346,19 @@ class DatabaseTable {
320
346
  // name a standalone prop after the column, but treat the column prop itself as not having FK.
321
347
  const columnName = nullableColumnsInFk[0];
322
348
  const baseName = this.getSafeBaseNameForFkProp(namingStrategy, currentFk, fks, columnName);
323
- fksOnStandaloneProps.set(baseName, [fkIndex, currentFk]);
324
- fkIndexes.set(fkIndex, { fk: currentFk, baseName });
349
+ standaloneFksBasedOnColumnNames.set(baseName, currentFk);
350
+ fksOnStandaloneProps.set(baseName, { fkIndex, currentFk });
351
+ if (fkIndex) {
352
+ fkIndexes.set(fkIndex, { fk: currentFk, baseName });
353
+ }
325
354
  continue;
326
355
  }
327
356
  // FK is not unambiguously mappable to a column. Pick another name for a standalone FK prop.
328
357
  const baseName = this.getSafeBaseNameForFkProp(namingStrategy, currentFk, fks);
329
- fksOnStandaloneProps.set(baseName, [fkIndex, currentFk]);
330
- fkIndexes.set(fkIndex, { fk: currentFk, baseName });
358
+ fksOnStandaloneProps.set(baseName, { fkIndex, currentFk });
359
+ if (fkIndex) {
360
+ fkIndexes.set(fkIndex, { fk: currentFk, baseName });
361
+ }
331
362
  }
332
363
  const columnsInFks = Object.keys(columnFks);
333
364
  const skippingHandlers = {
@@ -350,6 +381,41 @@ class DatabaseTable {
350
381
  },
351
382
  };
352
383
  const skippedColumnNames = this.getColumns().filter(skippingHandlers[scalarPropertiesForRelations]).map(column => column.name);
384
+ // Check standalone FKs named after columns for potential conflicts among themselves.
385
+ // This typically happens when two standalone FKs named after a column resolve to the same prop name
386
+ // because the respective columns include the referenced table in the name.
387
+ // Depending on naming strategy and actual names, it may also originate from other scenarios.
388
+ // We do our best to de-duplicate them here.
389
+ const safePropNames = new Set();
390
+ const unsafePropNames = new Map();
391
+ for (const [unsafeBaseName, currentFk] of standaloneFksBasedOnColumnNames) {
392
+ const propName = this.getPropertyName(namingStrategy, unsafeBaseName, currentFk);
393
+ if (safePropNames.has(propName)) {
394
+ if (!unsafePropNames.has(propName)) {
395
+ unsafePropNames.set(propName, []);
396
+ }
397
+ unsafePropNames.get(propName).push({ unsafeBaseName, currentFk });
398
+ continue;
399
+ }
400
+ safePropNames.add(propName);
401
+ }
402
+ for (const [unsafePropName, affectedBaseNames] of unsafePropNames) {
403
+ safePropNames.delete(unsafePropName);
404
+ for (const { unsafeBaseName, currentFk } of affectedBaseNames) {
405
+ const newBaseName = this.getSafeBaseNameForFkProp(namingStrategy, currentFk, fks);
406
+ fksOnStandaloneProps.delete(unsafeBaseName);
407
+ let fkIndex;
408
+ for (const [indexDef, fkIndexDesc] of fkIndexes) {
409
+ if (fkIndexDesc.fk !== currentFk) {
410
+ continue;
411
+ }
412
+ fkIndexDesc.baseName = newBaseName;
413
+ fkIndex = indexDef;
414
+ break;
415
+ }
416
+ fksOnStandaloneProps.set(newBaseName, { fkIndex, currentFk });
417
+ }
418
+ }
353
419
  return { fksOnColumnProps, fksOnStandaloneProps, columnFks, fkIndexes, nullableForeignKeys, skippedColumnNames };
354
420
  }
355
421
  findFkIndex(currentFk) {
@@ -369,7 +435,7 @@ class DatabaseTable {
369
435
  }
370
436
  return a.keyName.localeCompare(b.keyName);
371
437
  });
372
- return possibleIndexes[0];
438
+ return possibleIndexes.at(0);
373
439
  }
374
440
  getIndexProperties(index, columnFks, fksOnColumnProps, fksOnStandaloneProps, namingStrategy) {
375
441
  const propBaseNames = new Set();
@@ -381,9 +447,13 @@ class DatabaseTable {
381
447
  for (let i = 0; i < l; ++i) {
382
448
  const columnName = columnNames[i];
383
449
  // The column is not involved with FKs.
384
- // It has a prop named after it.
385
- // Add it and move on.
386
450
  if (!(columnName in columnFks)) {
451
+ // If there is no such column, the "name" is actually an expression.
452
+ if (!this.hasColumn(columnName)) {
453
+ return;
454
+ }
455
+ // It has a prop named after it.
456
+ // Add it and move on.
387
457
  propBaseNames.add(columnName);
388
458
  continue;
389
459
  }
@@ -398,7 +468,7 @@ class DatabaseTable {
398
468
  // and all of its columns are a subset of this index,
399
469
  // include that FK, and consider mapping of this column to a prop a success.
400
470
  let propAdded = false;
401
- for (const [propName, [, fk]] of fksOnStandaloneProps) {
471
+ for (const [propName, { currentFk: fk }] of fksOnStandaloneProps) {
402
472
  if (!columnFks[columnName].includes(fk)) {
403
473
  continue;
404
474
  }
@@ -473,7 +543,7 @@ class DatabaseTable {
473
543
  hasPrimaryKey() {
474
544
  return !!this.getPrimaryKey();
475
545
  }
476
- getForeignKeyDeclaration(fk, namingStrategy, schemaHelper, fkIndex, nullable, propNameBase) {
546
+ getForeignKeyDeclaration(fk, namingStrategy, schemaHelper, fkIndex, nullable, propNameBase, fksOnColumnProps) {
477
547
  const prop = this.getPropertyName(namingStrategy, propNameBase, fk);
478
548
  const kind = (fkIndex?.unique && !fkIndex.primary) ? this.getReferenceKind(fk, fkIndex) : this.getReferenceKind(fk);
479
549
  const runtimeType = this.getPropertyTypeForForeignKey(namingStrategy, fk);
@@ -499,6 +569,7 @@ class DatabaseTable {
499
569
  columnOptions.length = column.length;
500
570
  columnOptions.precision = column.precision;
501
571
  columnOptions.scale = column.scale;
572
+ columnOptions.extra = column.extra;
502
573
  columnOptions.comment = column.comment;
503
574
  columnOptions.enum = !!column.enumItems?.length;
504
575
  columnOptions.items = column.enumItems;
@@ -556,6 +627,7 @@ class DatabaseTable {
556
627
  length: column.length,
557
628
  precision: column.precision,
558
629
  scale: column.scale,
630
+ extra: column.extra,
559
631
  comment: column.comment,
560
632
  index: index ? index.keyName : undefined,
561
633
  unique: unique ? unique.keyName : undefined,