@mikro-orm/knex 6.3.5-dev.2 → 6.3.5-dev.20
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.
- package/AbstractSqlDriver.js +13 -2
- package/README.md +2 -1
- package/dialects/mysql/MySqlPlatform.d.ts +5 -0
- package/dialects/mysql/MySqlPlatform.js +7 -0
- package/dialects/mysql/MySqlSchemaHelper.js +1 -1
- package/dialects/sqlite/BaseSqlitePlatform.d.ts +5 -0
- package/dialects/sqlite/BaseSqlitePlatform.js +7 -0
- package/package.json +2 -2
- package/schema/DatabaseTable.js +121 -49
- package/schema/SchemaComparator.js +2 -2
package/AbstractSqlDriver.js
CHANGED
|
@@ -135,11 +135,19 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
135
135
|
.indexHint(options.indexHint)
|
|
136
136
|
.comment(options.comments)
|
|
137
137
|
.hintComment(options.hintComments);
|
|
138
|
+
const { first, last, before, after } = options;
|
|
139
|
+
const isCursorPagination = [first, last, before, after].some(v => v != null);
|
|
138
140
|
if (type !== query_1.QueryType.COUNT) {
|
|
139
|
-
qb.limit(options?.limit, options?.offset);
|
|
140
141
|
if (options.orderBy) {
|
|
141
|
-
|
|
142
|
+
if (isCursorPagination) {
|
|
143
|
+
const { orderBy: newOrderBy, where } = this.processCursorOptions(meta, options, options.orderBy);
|
|
144
|
+
qb.andWhere(where).orderBy(newOrderBy);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
qb.orderBy(options.orderBy);
|
|
148
|
+
}
|
|
142
149
|
}
|
|
150
|
+
qb.limit(options?.limit, options?.offset);
|
|
143
151
|
}
|
|
144
152
|
qb.where(where);
|
|
145
153
|
const kqb = qb.getKnexQuery(false).clear('select');
|
|
@@ -154,6 +162,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
154
162
|
if (type === query_1.QueryType.COUNT) {
|
|
155
163
|
return res[0].count;
|
|
156
164
|
}
|
|
165
|
+
if (isCursorPagination && !first && !!last) {
|
|
166
|
+
res.reverse();
|
|
167
|
+
}
|
|
157
168
|
return res.map(row => this.mapResult(row, meta));
|
|
158
169
|
}
|
|
159
170
|
mapResult(result, meta, populate = [], qb, map = {}) {
|
package/README.md
CHANGED
|
@@ -143,7 +143,7 @@ There is also auto-generated [CHANGELOG.md](CHANGELOG.md) file based on commit m
|
|
|
143
143
|
- [Using `QueryBuilder`](https://mikro-orm.io/docs/query-builder)
|
|
144
144
|
- [Preloading Deeply Nested Structures via populate](https://mikro-orm.io/docs/nested-populate)
|
|
145
145
|
- [Property Validation](https://mikro-orm.io/docs/property-validation)
|
|
146
|
-
- [Lifecycle Hooks](https://mikro-orm.io/docs/
|
|
146
|
+
- [Lifecycle Hooks](https://mikro-orm.io/docs/events#hooks)
|
|
147
147
|
- [Vanilla JS Support](https://mikro-orm.io/docs/usage-with-js)
|
|
148
148
|
- [Schema Generator](https://mikro-orm.io/docs/schema-generator)
|
|
149
149
|
- [Entity Generator](https://mikro-orm.io/docs/entity-generator)
|
|
@@ -163,6 +163,7 @@ You can find example integrations for some popular frameworks in the [`mikro-orm
|
|
|
163
163
|
- [NextJS + MySQL](https://github.com/jonahallibone/mikro-orm-nextjs)
|
|
164
164
|
- [Accounts.js REST and GraphQL authentication + SQLite](https://github.com/darkbasic/mikro-orm-accounts-example)
|
|
165
165
|
- [Nest + Shopify + PostgreSQL + GraphQL](https://github.com/Cloudshelf/Shopify_CSConnector)
|
|
166
|
+
- [Elysia.js + libSQL + Bun](https://github.com/mikro-orm/elysia-bun-example-app)
|
|
166
167
|
|
|
167
168
|
### JavaScript Examples
|
|
168
169
|
|
|
@@ -16,6 +16,11 @@ export declare class MySqlPlatform extends AbstractSqlPlatform {
|
|
|
16
16
|
convertJsonToDatabaseValue(value: unknown, context?: TransformContext): unknown;
|
|
17
17
|
getJsonIndexDefinition(index: IndexDef): string[];
|
|
18
18
|
getBooleanTypeDeclarationSQL(): string;
|
|
19
|
+
normalizeColumnType(type: string, options?: {
|
|
20
|
+
length?: number;
|
|
21
|
+
precision?: number;
|
|
22
|
+
scale?: number;
|
|
23
|
+
}): string;
|
|
19
24
|
getDefaultMappedType(type: string): Type<unknown>;
|
|
20
25
|
supportsUnsigned(): boolean;
|
|
21
26
|
/**
|
|
@@ -36,6 +36,13 @@ class MySqlPlatform extends AbstractSqlPlatform_1.AbstractSqlPlatform {
|
|
|
36
36
|
getBooleanTypeDeclarationSQL() {
|
|
37
37
|
return 'tinyint(1)';
|
|
38
38
|
}
|
|
39
|
+
normalizeColumnType(type, options = {}) {
|
|
40
|
+
const simpleType = this.extractSimpleType(type);
|
|
41
|
+
if (['decimal', 'numeric'].includes(simpleType)) {
|
|
42
|
+
return this.getDecimalTypeDeclarationSQL(options);
|
|
43
|
+
}
|
|
44
|
+
return type;
|
|
45
|
+
}
|
|
39
46
|
getDefaultMappedType(type) {
|
|
40
47
|
if (type === 'tinyint(1)') {
|
|
41
48
|
return super.getDefaultMappedType('boolean');
|
|
@@ -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);
|
|
@@ -36,6 +36,11 @@ export declare abstract class BaseSqlitePlatform extends AbstractSqlPlatform {
|
|
|
36
36
|
getVarcharTypeDeclarationSQL(column: {
|
|
37
37
|
length?: number;
|
|
38
38
|
}): string;
|
|
39
|
+
normalizeColumnType(type: string, options?: {
|
|
40
|
+
length?: number;
|
|
41
|
+
precision?: number;
|
|
42
|
+
scale?: number;
|
|
43
|
+
}): string;
|
|
39
44
|
convertsJsonAutomatically(): boolean;
|
|
40
45
|
/**
|
|
41
46
|
* This is used to narrow the value of Date properties as they will be stored as timestamps in sqlite.
|
|
@@ -44,6 +44,13 @@ class BaseSqlitePlatform extends AbstractSqlPlatform_1.AbstractSqlPlatform {
|
|
|
44
44
|
getVarcharTypeDeclarationSQL(column) {
|
|
45
45
|
return 'text';
|
|
46
46
|
}
|
|
47
|
+
normalizeColumnType(type, options = {}) {
|
|
48
|
+
const simpleType = this.extractSimpleType(type);
|
|
49
|
+
if (['varchar', 'text'].includes(simpleType)) {
|
|
50
|
+
return this.getVarcharTypeDeclarationSQL(options);
|
|
51
|
+
}
|
|
52
|
+
return simpleType;
|
|
53
|
+
}
|
|
47
54
|
convertsJsonAutomatically() {
|
|
48
55
|
return false;
|
|
49
56
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/knex",
|
|
3
|
-
"version": "6.3.5-dev.
|
|
3
|
+
"version": "6.3.5-dev.20",
|
|
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.
|
|
69
|
+
"@mikro-orm/core": "6.3.5-dev.20",
|
|
70
70
|
"better-sqlite3": "*",
|
|
71
71
|
"libsql": "*",
|
|
72
72
|
"mariadb": "*"
|
package/schema/DatabaseTable.js
CHANGED
|
@@ -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 = {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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 (
|
|
191
|
-
ret.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
|
|
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
|
|
202
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
262
|
-
|
|
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
|
-
|
|
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
|
-
|
|
294
|
-
|
|
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
|
-
|
|
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
|
-
|
|
311
|
-
|
|
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
|
-
|
|
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
|
-
|
|
324
|
-
|
|
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,
|
|
330
|
-
|
|
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,17 +381,49 @@ 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) {
|
|
356
422
|
const fkColumnsLength = currentFk.columnNames.length;
|
|
357
423
|
const possibleIndexes = this.indexes.filter(index => {
|
|
358
|
-
return index.columnNames.length
|
|
424
|
+
return index.columnNames.length === fkColumnsLength && !currentFk.columnNames.some((columnName, i) => index.columnNames[i] !== columnName);
|
|
359
425
|
});
|
|
360
426
|
possibleIndexes.sort((a, b) => {
|
|
361
|
-
if (a.columnNames.length !== b.columnNames.length) {
|
|
362
|
-
return a.columnNames.length < b.columnNames.length ? -1 : 1;
|
|
363
|
-
}
|
|
364
427
|
if (a.primary !== b.primary) {
|
|
365
428
|
return a.primary ? -1 : 1;
|
|
366
429
|
}
|
|
@@ -369,7 +432,7 @@ class DatabaseTable {
|
|
|
369
432
|
}
|
|
370
433
|
return a.keyName.localeCompare(b.keyName);
|
|
371
434
|
});
|
|
372
|
-
return possibleIndexes
|
|
435
|
+
return possibleIndexes.at(0);
|
|
373
436
|
}
|
|
374
437
|
getIndexProperties(index, columnFks, fksOnColumnProps, fksOnStandaloneProps, namingStrategy) {
|
|
375
438
|
const propBaseNames = new Set();
|
|
@@ -381,9 +444,13 @@ class DatabaseTable {
|
|
|
381
444
|
for (let i = 0; i < l; ++i) {
|
|
382
445
|
const columnName = columnNames[i];
|
|
383
446
|
// The column is not involved with FKs.
|
|
384
|
-
// It has a prop named after it.
|
|
385
|
-
// Add it and move on.
|
|
386
447
|
if (!(columnName in columnFks)) {
|
|
448
|
+
// If there is no such column, the "name" is actually an expression.
|
|
449
|
+
if (!this.hasColumn(columnName)) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
// It has a prop named after it.
|
|
453
|
+
// Add it and move on.
|
|
387
454
|
propBaseNames.add(columnName);
|
|
388
455
|
continue;
|
|
389
456
|
}
|
|
@@ -398,7 +465,7 @@ class DatabaseTable {
|
|
|
398
465
|
// and all of its columns are a subset of this index,
|
|
399
466
|
// include that FK, and consider mapping of this column to a prop a success.
|
|
400
467
|
let propAdded = false;
|
|
401
|
-
for (const [propName,
|
|
468
|
+
for (const [propName, { currentFk: fk }] of fksOnStandaloneProps) {
|
|
402
469
|
if (!columnFks[columnName].includes(fk)) {
|
|
403
470
|
continue;
|
|
404
471
|
}
|
|
@@ -473,7 +540,7 @@ class DatabaseTable {
|
|
|
473
540
|
hasPrimaryKey() {
|
|
474
541
|
return !!this.getPrimaryKey();
|
|
475
542
|
}
|
|
476
|
-
getForeignKeyDeclaration(fk, namingStrategy, schemaHelper, fkIndex, nullable, propNameBase) {
|
|
543
|
+
getForeignKeyDeclaration(fk, namingStrategy, schemaHelper, fkIndex, nullable, propNameBase, fksOnColumnProps) {
|
|
477
544
|
const prop = this.getPropertyName(namingStrategy, propNameBase, fk);
|
|
478
545
|
const kind = (fkIndex?.unique && !fkIndex.primary) ? this.getReferenceKind(fk, fkIndex) : this.getReferenceKind(fk);
|
|
479
546
|
const runtimeType = this.getPropertyTypeForForeignKey(namingStrategy, fk);
|
|
@@ -499,6 +566,7 @@ class DatabaseTable {
|
|
|
499
566
|
columnOptions.length = column.length;
|
|
500
567
|
columnOptions.precision = column.precision;
|
|
501
568
|
columnOptions.scale = column.scale;
|
|
569
|
+
columnOptions.extra = column.extra;
|
|
502
570
|
columnOptions.comment = column.comment;
|
|
503
571
|
columnOptions.enum = !!column.enumItems?.length;
|
|
504
572
|
columnOptions.items = column.enumItems;
|
|
@@ -523,6 +591,11 @@ class DatabaseTable {
|
|
|
523
591
|
const unique = compositeFkUniques[prop] || this.indexes.find(idx => idx.columnNames[0] === column.name && !idx.composite && idx.unique && !idx.primary);
|
|
524
592
|
const kind = this.getReferenceKind(fk, unique);
|
|
525
593
|
const runtimeType = this.getPropertyTypeForColumn(namingStrategy, column, fk);
|
|
594
|
+
const type = fk ? runtimeType : (core_1.Utils.keys(core_1.t).find(k => {
|
|
595
|
+
const typeInCoreMap = this.platform.getMappedType(k);
|
|
596
|
+
return (typeInCoreMap !== core_1.Type.getType(core_1.UnknownType) || k === 'unknown') && typeInCoreMap === column.mappedType;
|
|
597
|
+
}) ?? runtimeType);
|
|
598
|
+
const ignoreSchemaChanges = (type === 'unknown' && column.length) ? (column.extra ? ['type', 'extra'] : ['type']) : undefined;
|
|
526
599
|
const defaultRaw = this.getPropertyDefaultValue(schemaHelper, column, runtimeType, true);
|
|
527
600
|
const defaultParsed = this.getPropertyDefaultValue(schemaHelper, column, runtimeType);
|
|
528
601
|
const defaultTs = defaultRaw !== defaultParsed ? defaultParsed : undefined;
|
|
@@ -537,12 +610,10 @@ class DatabaseTable {
|
|
|
537
610
|
}
|
|
538
611
|
return {
|
|
539
612
|
name: prop,
|
|
540
|
-
type
|
|
541
|
-
const typeInCoreMap = this.platform.getMappedType(k);
|
|
542
|
-
return (typeInCoreMap !== core_1.Type.getType(core_1.UnknownType) || k === 'unknown') && typeInCoreMap === column.mappedType;
|
|
543
|
-
}) ?? runtimeType),
|
|
613
|
+
type,
|
|
544
614
|
runtimeType,
|
|
545
615
|
kind,
|
|
616
|
+
ignoreSchemaChanges,
|
|
546
617
|
generated: column.generated,
|
|
547
618
|
optional: defaultRaw !== 'null' || defaultTs != null || typeof column.generated !== 'undefined',
|
|
548
619
|
columnType: column.type,
|
|
@@ -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,
|
|
@@ -385,9 +385,9 @@ class SchemaComparator {
|
|
|
385
385
|
const changedProperties = new Set();
|
|
386
386
|
const fromProp = this.mapColumnToProperty({ ...fromColumn, autoincrement: false });
|
|
387
387
|
const toProp = this.mapColumnToProperty({ ...toColumn, autoincrement: false });
|
|
388
|
-
const fromColumnType = fromColumn.mappedType.getColumnType(fromProp, this.platform).toLowerCase();
|
|
388
|
+
const fromColumnType = this.platform.normalizeColumnType(fromColumn.mappedType.getColumnType(fromProp, this.platform).toLowerCase(), fromProp);
|
|
389
389
|
const fromNativeEnum = fromTable.nativeEnums[fromColumnType] ?? Object.values(fromTable.nativeEnums).find(e => e.name === fromColumnType && e.schema !== '*');
|
|
390
|
-
const toColumnType = toColumn.mappedType.getColumnType(toProp, this.platform).toLowerCase();
|
|
390
|
+
const toColumnType = this.platform.normalizeColumnType(toColumn.mappedType.getColumnType(toProp, this.platform).toLowerCase(), toProp);
|
|
391
391
|
const log = (msg, params) => {
|
|
392
392
|
if (tableName) {
|
|
393
393
|
const copy = core_1.Utils.copy(params);
|