@mikro-orm/knex 6.0.0-dev.14 → 6.0.0-dev.140
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/AbstractSqlConnection.d.ts +8 -4
- package/AbstractSqlConnection.js +30 -18
- package/AbstractSqlDriver.d.ts +21 -23
- package/AbstractSqlDriver.js +394 -225
- package/AbstractSqlPlatform.d.ts +5 -4
- package/AbstractSqlPlatform.js +34 -5
- package/MonkeyPatchable.d.ts +1 -0
- package/MonkeyPatchable.js +3 -0
- package/PivotCollectionPersister.d.ts +17 -0
- package/PivotCollectionPersister.js +128 -0
- package/README.md +64 -107
- package/SqlEntityManager.d.ts +2 -6
- package/SqlEntityManager.js +5 -9
- package/SqlEntityRepository.d.ts +7 -6
- package/SqlEntityRepository.js +11 -8
- package/index.mjs +24 -6
- package/package.json +8 -8
- package/query/ArrayCriteriaNode.d.ts +3 -3
- package/query/CriteriaNode.d.ts +9 -8
- package/query/CriteriaNode.js +17 -9
- package/query/CriteriaNodeFactory.d.ts +6 -6
- package/query/CriteriaNodeFactory.js +17 -13
- package/query/ObjectCriteriaNode.d.ts +3 -3
- package/query/ObjectCriteriaNode.js +14 -10
- package/query/QueryBuilder.d.ts +50 -21
- package/query/QueryBuilder.js +389 -132
- package/query/QueryBuilderHelper.d.ts +11 -16
- package/query/QueryBuilderHelper.js +194 -155
- package/query/ScalarCriteriaNode.d.ts +3 -2
- package/query/ScalarCriteriaNode.js +10 -6
- package/query/enums.d.ts +7 -0
- package/query/enums.js +10 -2
- package/schema/DatabaseSchema.d.ts +5 -2
- package/schema/DatabaseSchema.js +27 -9
- package/schema/DatabaseTable.d.ts +4 -2
- package/schema/DatabaseTable.js +67 -32
- package/schema/SchemaComparator.d.ts +3 -3
- package/schema/SchemaComparator.js +50 -30
- package/schema/SchemaHelper.d.ts +6 -4
- package/schema/SchemaHelper.js +18 -7
- package/schema/SqlSchemaGenerator.d.ts +3 -6
- package/schema/SqlSchemaGenerator.js +69 -34
- package/typings.d.ts +17 -10
package/query/enums.d.ts
CHANGED
|
@@ -6,3 +6,10 @@ export declare enum QueryType {
|
|
|
6
6
|
UPDATE = "UPDATE",
|
|
7
7
|
DELETE = "DELETE"
|
|
8
8
|
}
|
|
9
|
+
export declare enum JoinType {
|
|
10
|
+
leftJoin = "left join",
|
|
11
|
+
innerJoin = "inner join",
|
|
12
|
+
pivotJoin = "pivot join",
|
|
13
|
+
innerJoinLateral = "inner join lateral",
|
|
14
|
+
leftJoinLateral = "left join lateral"
|
|
15
|
+
}
|
package/query/enums.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.QueryType = void 0;
|
|
3
|
+
exports.JoinType = exports.QueryType = void 0;
|
|
4
4
|
var QueryType;
|
|
5
5
|
(function (QueryType) {
|
|
6
6
|
QueryType["TRUNCATE"] = "TRUNCATE";
|
|
@@ -9,4 +9,12 @@ var QueryType;
|
|
|
9
9
|
QueryType["INSERT"] = "INSERT";
|
|
10
10
|
QueryType["UPDATE"] = "UPDATE";
|
|
11
11
|
QueryType["DELETE"] = "DELETE";
|
|
12
|
-
})(QueryType
|
|
12
|
+
})(QueryType || (exports.QueryType = QueryType = {}));
|
|
13
|
+
var JoinType;
|
|
14
|
+
(function (JoinType) {
|
|
15
|
+
JoinType["leftJoin"] = "left join";
|
|
16
|
+
JoinType["innerJoin"] = "inner join";
|
|
17
|
+
JoinType["pivotJoin"] = "pivot join";
|
|
18
|
+
JoinType["innerJoinLateral"] = "inner join lateral";
|
|
19
|
+
JoinType["leftJoinLateral"] = "left join lateral";
|
|
20
|
+
})(JoinType || (exports.JoinType = JoinType = {}));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Configuration, type Dictionary, type EntityMetadata } from '@mikro-orm/core';
|
|
2
2
|
import { DatabaseTable } from './DatabaseTable';
|
|
3
3
|
import type { AbstractSqlConnection } from '../AbstractSqlConnection';
|
|
4
4
|
import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
|
|
@@ -10,14 +10,17 @@ export declare class DatabaseSchema {
|
|
|
10
10
|
readonly name: string;
|
|
11
11
|
private tables;
|
|
12
12
|
private namespaces;
|
|
13
|
+
private nativeEnums;
|
|
13
14
|
constructor(platform: AbstractSqlPlatform, name: string);
|
|
14
15
|
addTable(name: string, schema: string | undefined | null, comment?: string): DatabaseTable;
|
|
15
16
|
getTables(): DatabaseTable[];
|
|
16
17
|
getTable(name: string): DatabaseTable | undefined;
|
|
17
18
|
hasTable(name: string): boolean;
|
|
19
|
+
setNativeEnums(nativeEnums: Dictionary<unknown[]>): void;
|
|
20
|
+
getNativeEnums(): Dictionary<unknown[]>;
|
|
18
21
|
hasNamespace(namespace: string): boolean;
|
|
19
22
|
getNamespaces(): string[];
|
|
20
|
-
static create(connection: AbstractSqlConnection, platform: AbstractSqlPlatform, config: Configuration, schemaName?: string): Promise<DatabaseSchema>;
|
|
23
|
+
static create(connection: AbstractSqlConnection, platform: AbstractSqlPlatform, config: Configuration, schemaName?: string, schemas?: string[]): Promise<DatabaseSchema>;
|
|
21
24
|
static fromMetadata(metadata: EntityMetadata[], platform: AbstractSqlPlatform, config: Configuration, schemaName?: string): DatabaseSchema;
|
|
22
25
|
private static getSchemaName;
|
|
23
26
|
private static shouldHaveColumn;
|
package/schema/DatabaseSchema.js
CHANGED
|
@@ -7,15 +7,19 @@ const DatabaseTable_1 = require("./DatabaseTable");
|
|
|
7
7
|
* @internal
|
|
8
8
|
*/
|
|
9
9
|
class DatabaseSchema {
|
|
10
|
+
platform;
|
|
11
|
+
name;
|
|
12
|
+
tables = [];
|
|
13
|
+
namespaces = new Set();
|
|
14
|
+
nativeEnums = {}; // for postgres
|
|
10
15
|
constructor(platform, name) {
|
|
11
16
|
this.platform = platform;
|
|
12
17
|
this.name = name;
|
|
13
|
-
this.tables = [];
|
|
14
|
-
this.namespaces = new Set();
|
|
15
18
|
}
|
|
16
19
|
addTable(name, schema, comment) {
|
|
17
20
|
const namespaceName = schema ?? this.name;
|
|
18
21
|
const table = new DatabaseTable_1.DatabaseTable(this.platform, name, namespaceName);
|
|
22
|
+
table.nativeEnums = this.nativeEnums;
|
|
19
23
|
table.comment = comment;
|
|
20
24
|
this.tables.push(table);
|
|
21
25
|
if (namespaceName != null) {
|
|
@@ -32,24 +36,38 @@ class DatabaseSchema {
|
|
|
32
36
|
hasTable(name) {
|
|
33
37
|
return !!this.getTable(name);
|
|
34
38
|
}
|
|
39
|
+
setNativeEnums(nativeEnums) {
|
|
40
|
+
this.nativeEnums = nativeEnums;
|
|
41
|
+
this.tables.forEach(t => t.nativeEnums = nativeEnums);
|
|
42
|
+
}
|
|
43
|
+
getNativeEnums() {
|
|
44
|
+
return this.nativeEnums;
|
|
45
|
+
}
|
|
35
46
|
hasNamespace(namespace) {
|
|
36
47
|
return this.namespaces.has(namespace);
|
|
37
48
|
}
|
|
38
49
|
getNamespaces() {
|
|
39
50
|
return [...this.namespaces];
|
|
40
51
|
}
|
|
41
|
-
static async create(connection, platform, config, schemaName) {
|
|
42
|
-
const schema = new DatabaseSchema(platform, schemaName ?? config.get('schema'));
|
|
52
|
+
static async create(connection, platform, config, schemaName, schemas) {
|
|
53
|
+
const schema = new DatabaseSchema(platform, schemaName ?? config.get('schema') ?? platform.getDefaultSchemaName());
|
|
43
54
|
const allTables = await connection.execute(platform.getSchemaHelper().getListTablesSQL());
|
|
44
55
|
const parts = config.get('migrations').tableName.split('.');
|
|
45
56
|
const migrationsTableName = parts[1] ?? parts[0];
|
|
46
57
|
const migrationsSchemaName = parts.length > 1 ? parts[0] : config.get('schema', platform.getDefaultSchemaName());
|
|
47
58
|
const tables = allTables.filter(t => t.table_name !== migrationsTableName || (t.schema_name && t.schema_name !== migrationsSchemaName));
|
|
48
|
-
await platform.getSchemaHelper().loadInformationSchema(schema, connection, tables);
|
|
59
|
+
await platform.getSchemaHelper().loadInformationSchema(schema, connection, tables, schemas && schemas.length > 0 ? schemas : undefined);
|
|
49
60
|
return schema;
|
|
50
61
|
}
|
|
51
62
|
static fromMetadata(metadata, platform, config, schemaName) {
|
|
52
63
|
const schema = new DatabaseSchema(platform, schemaName ?? config.get('schema'));
|
|
64
|
+
const nativeEnums = {};
|
|
65
|
+
for (const meta of metadata) {
|
|
66
|
+
meta.props
|
|
67
|
+
.filter(prop => prop.nativeEnumName)
|
|
68
|
+
.forEach(prop => nativeEnums[prop.nativeEnumName] = prop.items?.map(val => '' + val) ?? []);
|
|
69
|
+
}
|
|
70
|
+
schema.setNativeEnums(nativeEnums);
|
|
53
71
|
for (const meta of metadata) {
|
|
54
72
|
const table = schema.addTable(meta.collection, this.getSchemaName(meta, config, schemaName));
|
|
55
73
|
table.comment = meta.comment;
|
|
@@ -75,18 +93,18 @@ class DatabaseSchema {
|
|
|
75
93
|
return (meta.schema === '*' ? schema : meta.schema) ?? config.get('schema');
|
|
76
94
|
}
|
|
77
95
|
static shouldHaveColumn(meta, prop) {
|
|
78
|
-
if (prop.persist === false ||
|
|
96
|
+
if (prop.persist === false || (prop.columnTypes?.length ?? 0) === 0) {
|
|
79
97
|
return false;
|
|
80
98
|
}
|
|
81
|
-
if (meta.pivotTable || (core_1.
|
|
99
|
+
if (meta.pivotTable || (prop.kind === core_1.ReferenceKind.EMBEDDED && prop.object)) {
|
|
82
100
|
return true;
|
|
83
101
|
}
|
|
84
102
|
const getRootProperty = (prop) => prop.embedded ? getRootProperty(meta.properties[prop.embedded[0]]) : prop;
|
|
85
103
|
const rootProp = getRootProperty(prop);
|
|
86
|
-
if (rootProp.
|
|
104
|
+
if (rootProp.kind === core_1.ReferenceKind.EMBEDDED) {
|
|
87
105
|
return prop === rootProp || !rootProp.object;
|
|
88
106
|
}
|
|
89
|
-
return [core_1.
|
|
107
|
+
return [core_1.ReferenceKind.SCALAR, core_1.ReferenceKind.MANY_TO_ONE].includes(prop.kind) || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && prop.owner);
|
|
90
108
|
}
|
|
91
109
|
toJSON() {
|
|
92
110
|
const { platform, namespaces, ...rest } = this;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Dictionary, type EntityMetadata, type EntityProperty, type NamingStrategy } from '@mikro-orm/core';
|
|
2
2
|
import type { SchemaHelper } from './SchemaHelper';
|
|
3
3
|
import type { CheckDef, Column, ForeignKey, IndexDef } from '../typings';
|
|
4
4
|
import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
|
|
@@ -13,10 +13,12 @@ export declare class DatabaseTable {
|
|
|
13
13
|
private indexes;
|
|
14
14
|
private checks;
|
|
15
15
|
private foreignKeys;
|
|
16
|
+
nativeEnums: Dictionary<unknown[]>;
|
|
16
17
|
comment?: string;
|
|
17
18
|
constructor(platform: AbstractSqlPlatform, name: string, schema?: string | undefined);
|
|
18
19
|
getColumns(): Column[];
|
|
19
20
|
getColumn(name: string): Column | undefined;
|
|
21
|
+
removeColumn(name: string): void;
|
|
20
22
|
getIndexes(): IndexDef[];
|
|
21
23
|
getChecks(): CheckDef[];
|
|
22
24
|
init(cols: Column[], indexes: IndexDef[] | undefined, checks: CheckDef<unknown>[] | undefined, pks: string[], fks?: Dictionary<ForeignKey>, enums?: Dictionary<string[]>): void;
|
|
@@ -37,7 +39,7 @@ export declare class DatabaseTable {
|
|
|
37
39
|
getPrimaryKey(): IndexDef | undefined;
|
|
38
40
|
hasPrimaryKey(): boolean;
|
|
39
41
|
private getPropertyDeclaration;
|
|
40
|
-
private
|
|
42
|
+
private getReferenceKind;
|
|
41
43
|
private getPropertyName;
|
|
42
44
|
private getPropertyType;
|
|
43
45
|
private getPropertyDefaultValue;
|
package/schema/DatabaseTable.js
CHANGED
|
@@ -6,14 +6,19 @@ const core_1 = require("@mikro-orm/core");
|
|
|
6
6
|
* @internal
|
|
7
7
|
*/
|
|
8
8
|
class DatabaseTable {
|
|
9
|
+
platform;
|
|
10
|
+
name;
|
|
11
|
+
schema;
|
|
12
|
+
columns = {};
|
|
13
|
+
indexes = [];
|
|
14
|
+
checks = [];
|
|
15
|
+
foreignKeys = {};
|
|
16
|
+
nativeEnums = {}; // for postgres
|
|
17
|
+
comment;
|
|
9
18
|
constructor(platform, name, schema) {
|
|
10
19
|
this.platform = platform;
|
|
11
20
|
this.name = name;
|
|
12
21
|
this.schema = schema;
|
|
13
|
-
this.columns = {};
|
|
14
|
-
this.indexes = [];
|
|
15
|
-
this.checks = [];
|
|
16
|
-
this.foreignKeys = {};
|
|
17
22
|
Object.defineProperties(this, {
|
|
18
23
|
platform: { enumerable: false, writable: true },
|
|
19
24
|
});
|
|
@@ -24,8 +29,11 @@ class DatabaseTable {
|
|
|
24
29
|
getColumn(name) {
|
|
25
30
|
return this.columns[name];
|
|
26
31
|
}
|
|
32
|
+
removeColumn(name) {
|
|
33
|
+
delete this.columns[name];
|
|
34
|
+
}
|
|
27
35
|
getIndexes() {
|
|
28
|
-
return this.indexes;
|
|
36
|
+
return core_1.Utils.removeDuplicates(this.indexes);
|
|
29
37
|
}
|
|
30
38
|
getChecks() {
|
|
31
39
|
return this.checks;
|
|
@@ -41,7 +49,7 @@ class DatabaseTable {
|
|
|
41
49
|
const type = v.name in enums ? 'enum' : v.type;
|
|
42
50
|
v.mappedType = this.platform.getMappedType(type);
|
|
43
51
|
v.default = v.default?.toString().startsWith('nextval(') ? null : v.default;
|
|
44
|
-
v.enumItems
|
|
52
|
+
v.enumItems ??= enums[v.name] || [];
|
|
45
53
|
o[v.name] = v;
|
|
46
54
|
return o;
|
|
47
55
|
}, {});
|
|
@@ -51,44 +59,51 @@ class DatabaseTable {
|
|
|
51
59
|
}
|
|
52
60
|
addColumnFromProperty(prop, meta) {
|
|
53
61
|
prop.fieldNames.forEach((field, idx) => {
|
|
54
|
-
var _a;
|
|
55
62
|
const type = prop.enum ? 'enum' : prop.columnTypes[idx];
|
|
56
63
|
const mappedType = this.platform.getMappedType(type);
|
|
57
64
|
if (mappedType instanceof core_1.DecimalType) {
|
|
58
65
|
const match = prop.columnTypes[idx].match(/\w+\((\d+), ?(\d+)\)/);
|
|
59
66
|
/* istanbul ignore else */
|
|
60
67
|
if (match) {
|
|
61
|
-
prop.precision
|
|
62
|
-
prop.scale
|
|
68
|
+
prop.precision ??= +match[1];
|
|
69
|
+
prop.scale ??= +match[2];
|
|
63
70
|
prop.length = undefined;
|
|
64
71
|
}
|
|
65
72
|
}
|
|
66
73
|
if (mappedType instanceof core_1.DateTimeType) {
|
|
67
|
-
|
|
74
|
+
const match = prop.columnTypes[idx].match(/\w+\((\d+)\)/);
|
|
75
|
+
if (match) {
|
|
76
|
+
prop.length ??= +match[1];
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
prop.length ??= this.platform.getDefaultDateTimeLength();
|
|
80
|
+
}
|
|
68
81
|
}
|
|
69
|
-
const primary = !meta.compositePK && !!prop.primary && prop.
|
|
82
|
+
const primary = !meta.compositePK && !!prop.primary && prop.kind === core_1.ReferenceKind.SCALAR && this.platform.isNumericColumn(mappedType);
|
|
70
83
|
this.columns[field] = {
|
|
71
84
|
name: prop.fieldNames[idx],
|
|
72
85
|
type: prop.columnTypes[idx],
|
|
86
|
+
generated: prop.generated,
|
|
73
87
|
mappedType,
|
|
74
88
|
unsigned: prop.unsigned && this.platform.isNumericColumn(mappedType),
|
|
75
89
|
autoincrement: prop.autoincrement ?? primary,
|
|
76
90
|
primary,
|
|
77
|
-
nullable: !!prop.nullable,
|
|
91
|
+
nullable: this.columns[field]?.nullable ?? !!prop.nullable,
|
|
92
|
+
nativeEnumName: prop.nativeEnumName,
|
|
78
93
|
length: prop.length,
|
|
79
94
|
precision: prop.precision,
|
|
80
95
|
scale: prop.scale,
|
|
81
96
|
default: prop.defaultRaw,
|
|
82
|
-
enumItems: prop.items?.every(core_1.Utils.isString) ? prop.items : undefined,
|
|
97
|
+
enumItems: prop.nativeEnumName || prop.items?.every(core_1.Utils.isString) ? prop.items : undefined,
|
|
83
98
|
comment: prop.comment,
|
|
84
99
|
extra: prop.extra,
|
|
85
100
|
ignoreSchemaChanges: prop.ignoreSchemaChanges,
|
|
86
101
|
};
|
|
87
|
-
|
|
102
|
+
this.columns[field].unsigned ||= this.columns[field].autoincrement;
|
|
88
103
|
const defaultValue = this.platform.getSchemaHelper().normalizeDefaultValue(prop.defaultRaw, prop.length);
|
|
89
104
|
this.columns[field].default = defaultValue;
|
|
90
105
|
});
|
|
91
|
-
if ([core_1.
|
|
106
|
+
if ([core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
92
107
|
const constraintName = this.getIndexName(true, prop.fieldNames, 'foreign');
|
|
93
108
|
let schema = prop.targetMeta.schema === '*' ? this.schema : prop.targetMeta.schema ?? this.schema;
|
|
94
109
|
if (prop.referencedTableName.includes('.')) {
|
|
@@ -102,11 +117,11 @@ class DatabaseTable {
|
|
|
102
117
|
referencedTableName: schema ? `${schema}.${prop.referencedTableName}` : prop.referencedTableName,
|
|
103
118
|
};
|
|
104
119
|
const cascade = prop.cascade.includes(core_1.Cascade.REMOVE) || prop.cascade.includes(core_1.Cascade.ALL);
|
|
105
|
-
if (prop.
|
|
106
|
-
this.foreignKeys[constraintName].deleteRule = prop.
|
|
120
|
+
if (prop.deleteRule || cascade || prop.nullable) {
|
|
121
|
+
this.foreignKeys[constraintName].deleteRule = prop.deleteRule || (cascade ? 'cascade' : 'set null');
|
|
107
122
|
}
|
|
108
|
-
if (prop.
|
|
109
|
-
this.foreignKeys[constraintName].updateRule = prop.
|
|
123
|
+
if (prop.updateRule || prop.cascade.includes(core_1.Cascade.PERSIST) || prop.cascade.includes(core_1.Cascade.ALL)) {
|
|
124
|
+
this.foreignKeys[constraintName].updateRule = prop.updateRule || 'cascade';
|
|
110
125
|
}
|
|
111
126
|
}
|
|
112
127
|
if (prop.index) {
|
|
@@ -114,6 +129,7 @@ class DatabaseTable {
|
|
|
114
129
|
columnNames: prop.fieldNames,
|
|
115
130
|
composite: prop.fieldNames.length > 1,
|
|
116
131
|
keyName: this.getIndexName(prop.index, prop.fieldNames, 'index'),
|
|
132
|
+
constraint: false,
|
|
117
133
|
primary: false,
|
|
118
134
|
unique: false,
|
|
119
135
|
});
|
|
@@ -123,6 +139,7 @@ class DatabaseTable {
|
|
|
123
139
|
columnNames: prop.fieldNames,
|
|
124
140
|
composite: prop.fieldNames.length > 1,
|
|
125
141
|
keyName: this.getIndexName(prop.unique, prop.fieldNames, 'unique'),
|
|
142
|
+
constraint: !prop.fieldNames.some((d) => d.includes('.')),
|
|
126
143
|
primary: false,
|
|
127
144
|
unique: true,
|
|
128
145
|
});
|
|
@@ -164,8 +181,8 @@ class DatabaseTable {
|
|
|
164
181
|
}
|
|
165
182
|
const meta = schema.init().meta;
|
|
166
183
|
meta.relations
|
|
167
|
-
.filter(prop => prop.primary && prop.
|
|
168
|
-
.forEach(prop => prop.
|
|
184
|
+
.filter(prop => prop.primary && prop.kind === core_1.ReferenceKind.MANY_TO_ONE && !meta.compositePK)
|
|
185
|
+
.forEach(prop => prop.kind = core_1.ReferenceKind.ONE_TO_ONE);
|
|
169
186
|
return meta;
|
|
170
187
|
}
|
|
171
188
|
/**
|
|
@@ -206,20 +223,20 @@ class DatabaseTable {
|
|
|
206
223
|
const prop = this.getPropertyName(namingStrategy, column);
|
|
207
224
|
const index = compositeFkIndexes[prop] || this.indexes.find(idx => idx.columnNames[0] === column.name && !idx.composite && !idx.unique && !idx.primary);
|
|
208
225
|
const unique = compositeFkUniques[prop] || this.indexes.find(idx => idx.columnNames[0] === column.name && !idx.composite && idx.unique && !idx.primary);
|
|
209
|
-
const
|
|
226
|
+
const kind = this.getReferenceKind(fk, unique);
|
|
210
227
|
const type = this.getPropertyType(namingStrategy, column, fk);
|
|
211
228
|
const fkOptions = {};
|
|
212
229
|
if (fk) {
|
|
213
230
|
fkOptions.fieldNames = fk.columnNames;
|
|
214
231
|
fkOptions.referencedTableName = fk.referencedTableName;
|
|
215
232
|
fkOptions.referencedColumnNames = fk.referencedColumnNames;
|
|
216
|
-
fkOptions.
|
|
217
|
-
fkOptions.
|
|
233
|
+
fkOptions.updateRule = fk.updateRule?.toLowerCase();
|
|
234
|
+
fkOptions.deleteRule = fk.deleteRule?.toLowerCase();
|
|
218
235
|
}
|
|
219
236
|
return {
|
|
220
237
|
name: prop,
|
|
221
238
|
type,
|
|
222
|
-
|
|
239
|
+
kind,
|
|
223
240
|
columnType: column.type,
|
|
224
241
|
default: this.getPropertyDefaultValue(schemaHelper, column, type),
|
|
225
242
|
defaultRaw: this.getPropertyDefaultValue(schemaHelper, column, type, true),
|
|
@@ -227,6 +244,8 @@ class DatabaseTable {
|
|
|
227
244
|
primary: column.primary,
|
|
228
245
|
fieldName: column.name,
|
|
229
246
|
length: column.length,
|
|
247
|
+
precision: column.precision,
|
|
248
|
+
scale: column.scale,
|
|
230
249
|
index: index ? index.keyName : undefined,
|
|
231
250
|
unique: unique ? unique.keyName : undefined,
|
|
232
251
|
enum: !!column.enumItems?.length,
|
|
@@ -234,14 +253,14 @@ class DatabaseTable {
|
|
|
234
253
|
...fkOptions,
|
|
235
254
|
};
|
|
236
255
|
}
|
|
237
|
-
|
|
256
|
+
getReferenceKind(fk, unique) {
|
|
238
257
|
if (fk && unique) {
|
|
239
|
-
return core_1.
|
|
258
|
+
return core_1.ReferenceKind.ONE_TO_ONE;
|
|
240
259
|
}
|
|
241
260
|
if (fk) {
|
|
242
|
-
return core_1.
|
|
261
|
+
return core_1.ReferenceKind.MANY_TO_ONE;
|
|
243
262
|
}
|
|
244
|
-
return core_1.
|
|
263
|
+
return core_1.ReferenceKind.SCALAR;
|
|
245
264
|
}
|
|
246
265
|
getPropertyName(namingStrategy, column) {
|
|
247
266
|
const fk = Object.values(this.foreignKeys).find(fk => fk.columnNames.includes(column.name));
|
|
@@ -288,7 +307,20 @@ class DatabaseTable {
|
|
|
288
307
|
return '' + val;
|
|
289
308
|
}
|
|
290
309
|
addIndex(meta, index, type) {
|
|
291
|
-
const properties = core_1.Utils.flatten(core_1.Utils.asArray(index.properties).map(prop =>
|
|
310
|
+
const properties = core_1.Utils.unique(core_1.Utils.flatten(core_1.Utils.asArray(index.properties).map(prop => {
|
|
311
|
+
const root = prop.replace(/\..+$/, '');
|
|
312
|
+
if (meta.properties[prop]) {
|
|
313
|
+
if (meta.properties[prop].embeddedPath) {
|
|
314
|
+
return [meta.properties[prop].embeddedPath.join('.')];
|
|
315
|
+
}
|
|
316
|
+
return meta.properties[prop].fieldNames;
|
|
317
|
+
}
|
|
318
|
+
// json index, we need to rename the column only
|
|
319
|
+
if (meta.properties[root]) {
|
|
320
|
+
return [prop.replace(root, meta.properties[root].fieldNames[0])];
|
|
321
|
+
}
|
|
322
|
+
return [prop];
|
|
323
|
+
})));
|
|
292
324
|
if (properties.length === 0 && !index.expression) {
|
|
293
325
|
return;
|
|
294
326
|
}
|
|
@@ -297,10 +329,13 @@ class DatabaseTable {
|
|
|
297
329
|
keyName: name,
|
|
298
330
|
columnNames: properties,
|
|
299
331
|
composite: properties.length > 1,
|
|
332
|
+
// JSON columns can have unique index but not unique constraint, and we need to distinguish those, so we can properly drop them
|
|
333
|
+
constraint: type !== 'index' && !properties.some((d) => d.includes('.')),
|
|
300
334
|
primary: type === 'primary',
|
|
301
335
|
unique: type !== 'index',
|
|
302
336
|
type: index.type,
|
|
303
337
|
expression: index.expression,
|
|
338
|
+
options: index.options,
|
|
304
339
|
});
|
|
305
340
|
}
|
|
306
341
|
addCheck(check) {
|
|
@@ -308,10 +343,10 @@ class DatabaseTable {
|
|
|
308
343
|
}
|
|
309
344
|
toJSON() {
|
|
310
345
|
const { platform, columns, ...rest } = this;
|
|
311
|
-
const columnsMapped =
|
|
346
|
+
const columnsMapped = core_1.Utils.keys(columns).reduce((o, col) => {
|
|
312
347
|
const { mappedType, ...restCol } = columns[col];
|
|
313
348
|
o[col] = restCol;
|
|
314
|
-
o[col].mappedType =
|
|
349
|
+
o[col].mappedType = core_1.Utils.keys(core_1.t).find(k => core_1.t[k] === mappedType.constructor);
|
|
315
350
|
return o;
|
|
316
351
|
}, {});
|
|
317
352
|
return { columns: columnsMapped, ...rest };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Column, ForeignKey, IndexDef, SchemaDifference, TableDifference } from '../typings';
|
|
2
2
|
import type { DatabaseSchema } from './DatabaseSchema';
|
|
3
3
|
import type { DatabaseTable } from './DatabaseTable';
|
|
4
4
|
import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
|
|
@@ -38,7 +38,7 @@ export declare class SchemaComparator {
|
|
|
38
38
|
* Returns the difference between the columns
|
|
39
39
|
* If there are differences this method returns field2, otherwise the boolean false.
|
|
40
40
|
*/
|
|
41
|
-
diffColumn(
|
|
41
|
+
diffColumn(fromColumn: Column, toColumn: Column, tableName?: string): Set<string>;
|
|
42
42
|
diffEnumItems(items1?: string[], items2?: string[]): boolean;
|
|
43
43
|
diffComment(comment1?: string, comment2?: string): boolean;
|
|
44
44
|
/**
|
|
@@ -50,7 +50,7 @@ export declare class SchemaComparator {
|
|
|
50
50
|
* Checks if the other index already fulfills all the indexing and constraint needs of the current one.
|
|
51
51
|
*/
|
|
52
52
|
isIndexFulfilledBy(index1: IndexDef, index2: IndexDef): boolean;
|
|
53
|
-
|
|
53
|
+
diffExpression(expr1: string, expr2: string): boolean;
|
|
54
54
|
hasSameDefaultValue(from: Column, to: Column): boolean;
|
|
55
55
|
private mapColumnToProperty;
|
|
56
56
|
private log;
|
|
@@ -7,6 +7,9 @@ const core_1 = require("@mikro-orm/core");
|
|
|
7
7
|
* Compares two Schemas and return an instance of SchemaDifference.
|
|
8
8
|
*/
|
|
9
9
|
class SchemaComparator {
|
|
10
|
+
platform;
|
|
11
|
+
helper;
|
|
12
|
+
logger;
|
|
10
13
|
constructor(platform) {
|
|
11
14
|
this.platform = platform;
|
|
12
15
|
this.helper = this.platform.getSchemaHelper();
|
|
@@ -139,6 +142,12 @@ class SchemaComparator {
|
|
|
139
142
|
if (changedProperties.size === 0) {
|
|
140
143
|
continue;
|
|
141
144
|
}
|
|
145
|
+
if (changedProperties.size === 1 && changedProperties.has('generated')) {
|
|
146
|
+
tableDifferences.addedColumns[column.name] = toTable.getColumn(column.name);
|
|
147
|
+
tableDifferences.removedColumns[column.name] = column;
|
|
148
|
+
changes++;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
142
151
|
tableDifferences.changedColumns[column.name] = {
|
|
143
152
|
oldColumnName: column.name,
|
|
144
153
|
fromColumn: column,
|
|
@@ -200,7 +209,7 @@ class SchemaComparator {
|
|
|
200
209
|
}
|
|
201
210
|
// See if index has changed in "to" table
|
|
202
211
|
const toTableCheck = toTable.getCheck(check.name);
|
|
203
|
-
if (!this.
|
|
212
|
+
if (!this.diffExpression(check.expression, toTableCheck.expression)) {
|
|
204
213
|
continue;
|
|
205
214
|
}
|
|
206
215
|
this.log(`check constraint ${check.name} changed in table ${tableDifferences.name}`, { fromTableCheck: check, toTableCheck });
|
|
@@ -324,12 +333,12 @@ class SchemaComparator {
|
|
|
324
333
|
* Returns the difference between the columns
|
|
325
334
|
* If there are differences this method returns field2, otherwise the boolean false.
|
|
326
335
|
*/
|
|
327
|
-
diffColumn(
|
|
336
|
+
diffColumn(fromColumn, toColumn, tableName) {
|
|
328
337
|
const changedProperties = new Set();
|
|
329
|
-
const
|
|
330
|
-
const
|
|
331
|
-
const
|
|
332
|
-
const
|
|
338
|
+
const fromProp = this.mapColumnToProperty({ ...fromColumn, autoincrement: false });
|
|
339
|
+
const toProp = this.mapColumnToProperty({ ...toColumn, autoincrement: false });
|
|
340
|
+
const fromColumnType = fromColumn.mappedType.getColumnType(fromProp, this.platform).toLowerCase();
|
|
341
|
+
const toColumnType = toColumn.mappedType.getColumnType(toProp, this.platform).toLowerCase();
|
|
333
342
|
const log = (msg, params) => {
|
|
334
343
|
if (tableName) {
|
|
335
344
|
const copy = core_1.Utils.copy(params);
|
|
@@ -337,40 +346,46 @@ class SchemaComparator {
|
|
|
337
346
|
this.log(msg, copy);
|
|
338
347
|
}
|
|
339
348
|
};
|
|
340
|
-
if (
|
|
341
|
-
!(
|
|
342
|
-
|
|
343
|
-
log(`'type' changed for column ${tableName}.${
|
|
349
|
+
if (fromColumnType !== toColumnType &&
|
|
350
|
+
!(fromColumn.ignoreSchemaChanges?.includes('type') ||
|
|
351
|
+
toColumn.ignoreSchemaChanges?.includes('type'))) {
|
|
352
|
+
log(`'type' changed for column ${tableName}.${fromColumn.name}`, { fromColumnType, toColumnType });
|
|
344
353
|
changedProperties.add('type');
|
|
345
354
|
}
|
|
346
|
-
if (
|
|
347
|
-
log(`'nullable' changed for column ${tableName}.${
|
|
355
|
+
if (fromColumn.nullable !== toColumn.nullable && !fromColumn.generated && !toColumn.generated) {
|
|
356
|
+
log(`'nullable' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
348
357
|
changedProperties.add('nullable');
|
|
349
358
|
}
|
|
350
|
-
if (
|
|
351
|
-
log(`'
|
|
359
|
+
if (this.diffExpression(fromColumn.generated, toColumn.generated)) {
|
|
360
|
+
log(`'generated' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
361
|
+
changedProperties.add('generated');
|
|
362
|
+
}
|
|
363
|
+
if (!!fromColumn.autoincrement !== !!toColumn.autoincrement) {
|
|
364
|
+
log(`'autoincrement' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
352
365
|
changedProperties.add('autoincrement');
|
|
353
366
|
}
|
|
354
|
-
if (
|
|
355
|
-
log(`'unsigned' changed for column ${tableName}.${
|
|
367
|
+
if (fromColumn.unsigned !== toColumn.unsigned && this.platform.supportsUnsigned()) {
|
|
368
|
+
log(`'unsigned' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
356
369
|
changedProperties.add('unsigned');
|
|
357
370
|
}
|
|
358
|
-
if (!this.hasSameDefaultValue(
|
|
359
|
-
log(`'default' changed for column ${tableName}.${
|
|
371
|
+
if (!this.hasSameDefaultValue(fromColumn, toColumn)) {
|
|
372
|
+
log(`'default' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
360
373
|
changedProperties.add('default');
|
|
361
374
|
}
|
|
362
|
-
if (this.diffComment(
|
|
363
|
-
log(`'comment' changed for column ${tableName}.${
|
|
375
|
+
if (this.diffComment(fromColumn.comment, toColumn.comment)) {
|
|
376
|
+
log(`'comment' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
364
377
|
changedProperties.add('comment');
|
|
365
378
|
}
|
|
366
|
-
if (
|
|
367
|
-
|
|
379
|
+
if (!(fromColumn.mappedType instanceof core_1.ArrayType) &&
|
|
380
|
+
!(toColumn.mappedType instanceof core_1.ArrayType) &&
|
|
381
|
+
this.diffEnumItems(fromColumn.enumItems, toColumn.enumItems)) {
|
|
382
|
+
log(`'enumItems' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
368
383
|
changedProperties.add('enumItems');
|
|
369
384
|
}
|
|
370
|
-
if ((
|
|
371
|
-
!(
|
|
372
|
-
|
|
373
|
-
log(`'extra' changed for column ${tableName}.${
|
|
385
|
+
if ((fromColumn.extra || '').toLowerCase() !== (toColumn.extra || '').toLowerCase() &&
|
|
386
|
+
!(fromColumn.ignoreSchemaChanges?.includes('extra') ||
|
|
387
|
+
toColumn.ignoreSchemaChanges?.includes('extra'))) {
|
|
388
|
+
log(`'extra' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
374
389
|
changedProperties.add('extra');
|
|
375
390
|
}
|
|
376
391
|
return changedProperties;
|
|
@@ -424,11 +439,11 @@ class SchemaComparator {
|
|
|
424
439
|
}
|
|
425
440
|
return index1.primary === index2.primary && index1.unique === index2.unique;
|
|
426
441
|
}
|
|
427
|
-
|
|
428
|
-
// check
|
|
442
|
+
diffExpression(expr1, expr2) {
|
|
443
|
+
// expressions like check constraints might be normalized by the driver,
|
|
429
444
|
// e.g. quotes might be added (https://github.com/mikro-orm/mikro-orm/issues/3827)
|
|
430
|
-
const simplify = (str) => str?.replace(/['"`()]
|
|
431
|
-
return simplify(
|
|
445
|
+
const simplify = (str) => str?.replace(/_\w+\\'(.*?)\\'/g, '$1').replace(/['"`()]|::\w+| +/g, '').toLowerCase();
|
|
446
|
+
return simplify(expr1) !== simplify(expr2);
|
|
432
447
|
}
|
|
433
448
|
hasSameDefaultValue(from, to) {
|
|
434
449
|
if (from.default == null || from.default.toString().toLowerCase() === 'null' || from.default.toString().startsWith('nextval(')) {
|
|
@@ -439,6 +454,11 @@ class SchemaComparator {
|
|
|
439
454
|
const defaultValueTo = !['0', 'false', 'f', 'n', 'no', 'off'].includes('' + to.default);
|
|
440
455
|
return defaultValueFrom === defaultValueTo;
|
|
441
456
|
}
|
|
457
|
+
if (to.mappedType instanceof core_1.JsonType) {
|
|
458
|
+
const defaultValueFrom = (0, core_1.parseJsonSafe)(from.default.replace(/^'(.*)'$/, '$1'));
|
|
459
|
+
const defaultValueTo = (0, core_1.parseJsonSafe)(to.default?.replace(/^'(.*)'$/, '$1'));
|
|
460
|
+
return core_1.Utils.equals(defaultValueFrom, defaultValueTo);
|
|
461
|
+
}
|
|
442
462
|
if (to.mappedType instanceof core_1.DateTimeType && from.default && to.default) {
|
|
443
463
|
// normalize now/current_timestamp defaults, also remove `()` from the end of default expression
|
|
444
464
|
const defaultValueFrom = from.default.toLowerCase().replace('current_timestamp', 'now').replace(/\(\)$/, '');
|
package/schema/SchemaHelper.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Knex } from 'knex';
|
|
2
|
-
import type
|
|
2
|
+
import { type Connection, type Dictionary } from '@mikro-orm/core';
|
|
3
3
|
import type { AbstractSqlConnection } from '../AbstractSqlConnection';
|
|
4
4
|
import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
|
|
5
5
|
import type { CheckDef, Column, IndexDef, Table, TableDifference } from '../typings';
|
|
@@ -18,7 +18,9 @@ export declare abstract class SchemaHelper {
|
|
|
18
18
|
getForeignKeys(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<Dictionary>;
|
|
19
19
|
protected getTableKey(t: Table): string;
|
|
20
20
|
getEnumDefinitions(connection: AbstractSqlConnection, checks: CheckDef[], tableName: string, schemaName?: string): Promise<Dictionary<string[]>>;
|
|
21
|
-
|
|
21
|
+
getDropNativeEnumSQL(name: string, schema?: string): string;
|
|
22
|
+
getAlterNativeEnumSQL(name: string, schema?: string, value?: string): string;
|
|
23
|
+
loadInformationSchema(schema: DatabaseSchema, connection: AbstractSqlConnection, tables: Table[], schemas?: string[]): Promise<void>;
|
|
22
24
|
getListTablesSQL(schemaName?: string): string;
|
|
23
25
|
getRenameColumnSQL(tableName: string, oldColumnName: string, to: Column, schemaName?: string): string;
|
|
24
26
|
getCreateIndexSQL(tableName: string, index: IndexDef): string;
|
|
@@ -29,8 +31,8 @@ export declare abstract class SchemaHelper {
|
|
|
29
31
|
configureColumn(column: Column, col: Knex.ColumnBuilder, knex: Knex, changedProperties?: Set<string>): Knex.ColumnBuilder;
|
|
30
32
|
configureColumnDefault(column: Column, col: Knex.ColumnBuilder, knex: Knex, changedProperties?: Set<string>): Knex.ColumnBuilder;
|
|
31
33
|
getPreAlterTable(tableDiff: TableDifference, safe: boolean): string;
|
|
32
|
-
getAlterColumnAutoincrement(tableName: string, column: Column): string;
|
|
33
|
-
getChangeColumnCommentSQL(tableName: string, to: Column): string;
|
|
34
|
+
getAlterColumnAutoincrement(tableName: string, column: Column, schemaName?: string): string;
|
|
35
|
+
getChangeColumnCommentSQL(tableName: string, to: Column, schemaName?: string): string;
|
|
34
36
|
getNamespaces(connection: AbstractSqlConnection): Promise<string[]>;
|
|
35
37
|
getColumns(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<Column[]>;
|
|
36
38
|
getIndexes(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<IndexDef[]>;
|