@mikro-orm/sql 7.0.3 → 7.0.4-dev.1
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 +58 -94
- package/AbstractSqlConnection.js +238 -235
- package/AbstractSqlDriver.d.ts +155 -410
- package/AbstractSqlDriver.js +1941 -2064
- package/AbstractSqlPlatform.d.ts +73 -83
- package/AbstractSqlPlatform.js +158 -162
- package/PivotCollectionPersister.d.ts +15 -33
- package/PivotCollectionPersister.js +160 -158
- package/README.md +1 -1
- package/SqlEntityManager.d.ts +22 -67
- package/SqlEntityManager.js +38 -54
- package/SqlEntityRepository.d.ts +14 -14
- package/SqlEntityRepository.js +23 -23
- package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +12 -12
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +194 -192
- package/dialects/mysql/BaseMySqlPlatform.d.ts +45 -64
- package/dialects/mysql/BaseMySqlPlatform.js +131 -134
- package/dialects/mysql/MySqlExceptionConverter.d.ts +6 -6
- package/dialects/mysql/MySqlExceptionConverter.js +77 -91
- package/dialects/mysql/MySqlNativeQueryBuilder.d.ts +3 -3
- package/dialects/mysql/MySqlNativeQueryBuilder.js +69 -66
- package/dialects/mysql/MySqlSchemaHelper.d.ts +39 -39
- package/dialects/mysql/MySqlSchemaHelper.js +319 -327
- package/dialects/oracledb/OracleDialect.d.ts +52 -81
- package/dialects/oracledb/OracleDialect.js +149 -155
- package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +12 -12
- package/dialects/oracledb/OracleNativeQueryBuilder.js +236 -232
- package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +105 -108
- package/dialects/postgresql/BasePostgreSqlPlatform.js +350 -351
- package/dialects/postgresql/FullTextType.d.ts +6 -10
- package/dialects/postgresql/FullTextType.js +51 -51
- package/dialects/postgresql/PostgreSqlExceptionConverter.d.ts +5 -5
- package/dialects/postgresql/PostgreSqlExceptionConverter.js +43 -55
- package/dialects/postgresql/PostgreSqlNativeQueryBuilder.d.ts +1 -1
- package/dialects/postgresql/PostgreSqlNativeQueryBuilder.js +4 -4
- package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +82 -102
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +705 -733
- package/dialects/sqlite/BaseSqliteConnection.d.ts +5 -3
- package/dialects/sqlite/BaseSqliteConnection.js +19 -21
- package/dialects/sqlite/NodeSqliteDialect.d.ts +1 -1
- package/dialects/sqlite/NodeSqliteDialect.js +23 -23
- package/dialects/sqlite/SqliteDriver.d.ts +1 -1
- package/dialects/sqlite/SqliteDriver.js +3 -3
- package/dialects/sqlite/SqliteExceptionConverter.d.ts +6 -6
- package/dialects/sqlite/SqliteExceptionConverter.js +51 -67
- package/dialects/sqlite/SqliteNativeQueryBuilder.d.ts +2 -2
- package/dialects/sqlite/SqliteNativeQueryBuilder.js +7 -7
- package/dialects/sqlite/SqlitePlatform.d.ts +72 -63
- package/dialects/sqlite/SqlitePlatform.js +139 -139
- package/dialects/sqlite/SqliteSchemaHelper.d.ts +60 -70
- package/dialects/sqlite/SqliteSchemaHelper.js +520 -533
- package/package.json +3 -3
- package/plugin/index.d.ts +35 -42
- package/plugin/index.js +36 -43
- package/plugin/transformer.d.ts +94 -117
- package/plugin/transformer.js +881 -890
- package/query/ArrayCriteriaNode.d.ts +4 -4
- package/query/ArrayCriteriaNode.js +18 -18
- package/query/CriteriaNode.d.ts +25 -35
- package/query/CriteriaNode.js +123 -133
- package/query/CriteriaNodeFactory.d.ts +6 -49
- package/query/CriteriaNodeFactory.js +94 -97
- package/query/NativeQueryBuilder.d.ts +118 -118
- package/query/NativeQueryBuilder.js +480 -484
- package/query/ObjectCriteriaNode.d.ts +12 -12
- package/query/ObjectCriteriaNode.js +282 -298
- package/query/QueryBuilder.d.ts +904 -1546
- package/query/QueryBuilder.js +2164 -2294
- package/query/QueryBuilderHelper.d.ts +72 -153
- package/query/QueryBuilderHelper.js +1028 -1079
- package/query/ScalarCriteriaNode.d.ts +3 -3
- package/query/ScalarCriteriaNode.js +46 -53
- package/query/enums.d.ts +14 -14
- package/query/enums.js +14 -14
- package/query/raw.d.ts +6 -16
- package/query/raw.js +10 -10
- package/schema/DatabaseSchema.d.ts +50 -73
- package/schema/DatabaseSchema.js +307 -331
- package/schema/DatabaseTable.d.ts +73 -96
- package/schema/DatabaseTable.js +927 -1012
- package/schema/SchemaComparator.d.ts +66 -70
- package/schema/SchemaComparator.js +740 -766
- package/schema/SchemaHelper.d.ts +95 -109
- package/schema/SchemaHelper.js +659 -675
- package/schema/SqlSchemaGenerator.d.ts +58 -78
- package/schema/SqlSchemaGenerator.js +501 -535
- package/typings.d.ts +266 -380
|
@@ -1,553 +1,519 @@
|
|
|
1
|
-
import { CommitOrderCalculator, TableNotFoundException, Utils } from '@mikro-orm/core';
|
|
1
|
+
import { CommitOrderCalculator, TableNotFoundException, Utils, } from '@mikro-orm/core';
|
|
2
2
|
import { AbstractSchemaGenerator } from '@mikro-orm/core/schema';
|
|
3
3
|
import { DatabaseSchema } from './DatabaseSchema.js';
|
|
4
4
|
import { SchemaComparator } from './SchemaComparator.js';
|
|
5
5
|
/** Generates and manages SQL database schemas based on entity metadata. Supports create, update, and drop operations. */
|
|
6
6
|
export class SqlSchemaGenerator extends AbstractSchemaGenerator {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
/* v8 ignore next */
|
|
42
|
-
if (options?.clear) {
|
|
43
|
-
await this.clear({ ...options, clearIdentityMap: false });
|
|
44
|
-
}
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
getTargetSchema(schema) {
|
|
48
|
-
const metadata = this.getOrderedMetadata(schema);
|
|
49
|
-
const schemaName = schema ?? this.config.get('schema') ?? this.platform.getDefaultSchemaName();
|
|
50
|
-
return DatabaseSchema.fromMetadata(metadata, this.platform, this.config, schemaName, this.em);
|
|
51
|
-
}
|
|
52
|
-
getOrderedMetadata(schema) {
|
|
53
|
-
const metadata = super.getOrderedMetadata(schema);
|
|
54
|
-
// Filter out skipped tables
|
|
55
|
-
return metadata.filter(meta => {
|
|
56
|
-
const tableName = meta.tableName;
|
|
57
|
-
const tableSchema = meta.schema ?? schema ?? this.config.get('schema');
|
|
58
|
-
return !this.isTableSkipped(tableName, tableSchema);
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
async getCreateSchemaSQL(options = {}) {
|
|
62
|
-
const toSchema = this.getTargetSchema(options.schema);
|
|
63
|
-
const ret = [];
|
|
64
|
-
for (const namespace of toSchema.getNamespaces()) {
|
|
65
|
-
if (namespace === this.platform.getDefaultSchemaName()) {
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
const sql = this.helper.getCreateNamespaceSQL(namespace);
|
|
69
|
-
this.append(ret, sql);
|
|
70
|
-
}
|
|
71
|
-
if (this.platform.supportsNativeEnums()) {
|
|
72
|
-
const created = [];
|
|
73
|
-
for (const [enumName, enumOptions] of Object.entries(toSchema.getNativeEnums())) {
|
|
7
|
+
helper = this.platform.getSchemaHelper();
|
|
8
|
+
options = this.config.get('schemaGenerator');
|
|
9
|
+
lastEnsuredDatabase;
|
|
10
|
+
static register(orm) {
|
|
11
|
+
orm.config.registerExtension('@mikro-orm/schema-generator', () => new SqlSchemaGenerator(orm.em));
|
|
12
|
+
}
|
|
13
|
+
async create(options) {
|
|
14
|
+
await this.ensureDatabase();
|
|
15
|
+
const sql = await this.getCreateSchemaSQL(options);
|
|
16
|
+
await this.execute(sql);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Returns true if the database was created.
|
|
20
|
+
*/
|
|
21
|
+
async ensureDatabase(options) {
|
|
22
|
+
await this.connection.ensureConnection();
|
|
23
|
+
const dbName = this.config.get('dbName');
|
|
24
|
+
if (this.lastEnsuredDatabase === dbName && !options?.forceCheck) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
const exists = await this.helper.databaseExists(this.connection, dbName);
|
|
28
|
+
this.lastEnsuredDatabase = dbName;
|
|
29
|
+
if (!exists) {
|
|
30
|
+
const managementDbName = this.helper.getManagementDbName();
|
|
31
|
+
if (managementDbName) {
|
|
32
|
+
this.config.set('dbName', managementDbName);
|
|
33
|
+
await this.driver.reconnect({ skipOnConnect: true });
|
|
34
|
+
await this.createDatabase(dbName, { skipOnConnect: true });
|
|
35
|
+
}
|
|
36
|
+
if (options?.create) {
|
|
37
|
+
await this.create(options);
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
74
41
|
/* v8 ignore next */
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
for (const meta of this.getOrderedMetadata(schema).reverse()) {
|
|
138
|
-
try {
|
|
139
|
-
await this.driver
|
|
140
|
-
.createQueryBuilder(meta.class, this.em?.getTransactionContext(), 'write', false)
|
|
141
|
-
.withSchema(schema)
|
|
142
|
-
.truncate()
|
|
143
|
-
.execute();
|
|
144
|
-
} catch (e) {
|
|
145
|
-
if (this.platform.getExceptionConverter().convertException(e) instanceof TableNotFoundException) {
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
throw e;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
if (this.options.disableForeignKeysForClear) {
|
|
152
|
-
await this.execute(this.helper.enableForeignKeysSQL());
|
|
153
|
-
}
|
|
154
|
-
if (options?.clearIdentityMap ?? true) {
|
|
155
|
-
this.clearIdentityMap();
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
async getDropSchemaSQL(options = {}) {
|
|
159
|
-
await this.ensureDatabase();
|
|
160
|
-
const metadata = this.getOrderedMetadata(options.schema).reverse();
|
|
161
|
-
const schemas = this.getTargetSchema(options.schema).getNamespaces();
|
|
162
|
-
const schema = await DatabaseSchema.create(
|
|
163
|
-
this.connection,
|
|
164
|
-
this.platform,
|
|
165
|
-
this.config,
|
|
166
|
-
options.schema,
|
|
167
|
-
schemas,
|
|
168
|
-
undefined,
|
|
169
|
-
this.options.skipTables,
|
|
170
|
-
this.options.skipViews,
|
|
171
|
-
);
|
|
172
|
-
const ret = [];
|
|
173
|
-
// Drop views first (views may depend on tables)
|
|
174
|
-
// Drop in reverse dependency order (dependent views first)
|
|
175
|
-
const targetSchema = this.getTargetSchema(options.schema);
|
|
176
|
-
const sortedViews = this.sortViewsByDependencies(targetSchema.getViews()).reverse();
|
|
177
|
-
for (const view of sortedViews) {
|
|
178
|
-
if (view.materialized) {
|
|
179
|
-
this.append(ret, this.helper.dropMaterializedViewIfExists(view.name, view.schema));
|
|
180
|
-
} else {
|
|
181
|
-
this.append(ret, this.helper.dropViewIfExists(view.name, view.schema));
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
// remove FKs explicitly if we can't use a cascading statement and we don't disable FK checks (we need this for circular relations)
|
|
185
|
-
for (const meta of metadata) {
|
|
186
|
-
if (!this.platform.usesCascadeStatement() && (!this.options.disableForeignKeys || options.dropForeignKeys)) {
|
|
187
|
-
const table = schema.getTable(meta.tableName);
|
|
188
|
-
if (!table) {
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
const foreignKeys = Object.values(table.getForeignKeys());
|
|
192
|
-
for (const fk of foreignKeys) {
|
|
193
|
-
this.append(ret, this.helper.dropForeignKey(table.getShortestName(), fk.constraintName));
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
for (const meta of metadata) {
|
|
198
|
-
this.append(ret, this.helper.dropTableIfExists(meta.tableName, this.getSchemaName(meta, options)));
|
|
199
|
-
}
|
|
200
|
-
if (this.platform.supportsNativeEnums()) {
|
|
201
|
-
for (const columnName of Object.keys(schema.getNativeEnums())) {
|
|
202
|
-
const sql = this.helper.getDropNativeEnumSQL(columnName, options.schema ?? this.config.get('schema'));
|
|
203
|
-
this.append(ret, sql);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
if (options.dropMigrationsTable) {
|
|
207
|
-
this.append(
|
|
208
|
-
ret,
|
|
209
|
-
this.helper.dropTableIfExists(this.config.get('migrations').tableName, this.config.get('schema')),
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
return this.wrapSchema(ret, options);
|
|
213
|
-
}
|
|
214
|
-
getSchemaName(meta, options) {
|
|
215
|
-
const schemaName = options.schema ?? this.config.get('schema');
|
|
216
|
-
/* v8 ignore next */
|
|
217
|
-
const resolvedName = meta.schema && meta.schema === '*' ? schemaName : (meta.schema ?? schemaName);
|
|
218
|
-
// skip default schema name
|
|
219
|
-
if (resolvedName === this.platform.getDefaultSchemaName()) {
|
|
220
|
-
return undefined;
|
|
221
|
-
}
|
|
222
|
-
return resolvedName;
|
|
223
|
-
}
|
|
224
|
-
async update(options = {}) {
|
|
225
|
-
const sql = await this.getUpdateSchemaSQL(options);
|
|
226
|
-
await this.execute(sql);
|
|
227
|
-
}
|
|
228
|
-
async getUpdateSchemaSQL(options = {}) {
|
|
229
|
-
await this.ensureDatabase();
|
|
230
|
-
const { fromSchema, toSchema } = await this.prepareSchemaForComparison(options);
|
|
231
|
-
const comparator = new SchemaComparator(this.platform);
|
|
232
|
-
const diffUp = comparator.compare(fromSchema, toSchema);
|
|
233
|
-
return this.diffToSQL(diffUp, options);
|
|
234
|
-
}
|
|
235
|
-
async getUpdateSchemaMigrationSQL(options = {}) {
|
|
236
|
-
if (!options.fromSchema) {
|
|
237
|
-
await this.ensureDatabase();
|
|
238
|
-
}
|
|
239
|
-
const { fromSchema, toSchema } = await this.prepareSchemaForComparison(options);
|
|
240
|
-
const comparator = new SchemaComparator(this.platform);
|
|
241
|
-
const diffUp = comparator.compare(fromSchema, toSchema);
|
|
242
|
-
const diffDown = comparator.compare(toSchema, fromSchema, diffUp);
|
|
243
|
-
return {
|
|
244
|
-
up: this.diffToSQL(diffUp, options),
|
|
245
|
-
down: this.platform.supportsDownMigrations() ? this.diffToSQL(diffDown, options) : '',
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
async prepareSchemaForComparison(options) {
|
|
249
|
-
options.safe ??= false;
|
|
250
|
-
options.dropTables ??= true;
|
|
251
|
-
const toSchema = this.getTargetSchema(options.schema);
|
|
252
|
-
const schemas = toSchema.getNamespaces();
|
|
253
|
-
const fromSchema =
|
|
254
|
-
options.fromSchema ??
|
|
255
|
-
(await DatabaseSchema.create(
|
|
256
|
-
this.connection,
|
|
257
|
-
this.platform,
|
|
258
|
-
this.config,
|
|
259
|
-
options.schema,
|
|
260
|
-
schemas,
|
|
261
|
-
undefined,
|
|
262
|
-
this.options.skipTables,
|
|
263
|
-
this.options.skipViews,
|
|
264
|
-
));
|
|
265
|
-
const wildcardSchemaTables = [...this.metadata.getAll().values()]
|
|
266
|
-
.filter(meta => meta.schema === '*')
|
|
267
|
-
.map(meta => meta.tableName);
|
|
268
|
-
fromSchema.prune(options.schema, wildcardSchemaTables);
|
|
269
|
-
toSchema.prune(options.schema, wildcardSchemaTables);
|
|
270
|
-
return { fromSchema, toSchema };
|
|
271
|
-
}
|
|
272
|
-
diffToSQL(schemaDiff, options) {
|
|
273
|
-
const ret = [];
|
|
274
|
-
globalThis.idx = 0;
|
|
275
|
-
if (this.platform.supportsSchemas()) {
|
|
276
|
-
for (const newNamespace of schemaDiff.newNamespaces) {
|
|
277
|
-
const sql = this.helper.getCreateNamespaceSQL(newNamespace);
|
|
278
|
-
this.append(ret, sql);
|
|
279
|
-
}
|
|
42
|
+
if (options?.clear) {
|
|
43
|
+
await this.clear({ ...options, clearIdentityMap: false });
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
getTargetSchema(schema) {
|
|
48
|
+
const metadata = this.getOrderedMetadata(schema);
|
|
49
|
+
const schemaName = schema ?? this.config.get('schema') ?? this.platform.getDefaultSchemaName();
|
|
50
|
+
return DatabaseSchema.fromMetadata(metadata, this.platform, this.config, schemaName, this.em);
|
|
51
|
+
}
|
|
52
|
+
getOrderedMetadata(schema) {
|
|
53
|
+
const metadata = super.getOrderedMetadata(schema);
|
|
54
|
+
// Filter out skipped tables
|
|
55
|
+
return metadata.filter(meta => {
|
|
56
|
+
const tableName = meta.tableName;
|
|
57
|
+
const tableSchema = meta.schema ?? schema ?? this.config.get('schema');
|
|
58
|
+
return !this.isTableSkipped(tableName, tableSchema);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
async getCreateSchemaSQL(options = {}) {
|
|
62
|
+
const toSchema = this.getTargetSchema(options.schema);
|
|
63
|
+
const ret = [];
|
|
64
|
+
for (const namespace of toSchema.getNamespaces()) {
|
|
65
|
+
if (namespace === this.platform.getDefaultSchemaName()) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const sql = this.helper.getCreateNamespaceSQL(namespace);
|
|
69
|
+
this.append(ret, sql);
|
|
70
|
+
}
|
|
71
|
+
if (this.platform.supportsNativeEnums()) {
|
|
72
|
+
const created = [];
|
|
73
|
+
for (const [enumName, enumOptions] of Object.entries(toSchema.getNativeEnums())) {
|
|
74
|
+
/* v8 ignore next */
|
|
75
|
+
if (created.includes(enumName)) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
created.push(enumName);
|
|
79
|
+
const sql = this.helper.getCreateNativeEnumSQL(enumOptions.name, enumOptions.items, this.getSchemaName(enumOptions, options));
|
|
80
|
+
this.append(ret, sql);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const table of toSchema.getTables()) {
|
|
84
|
+
this.append(ret, this.helper.createTable(table), true);
|
|
85
|
+
}
|
|
86
|
+
if (this.helper.supportsSchemaConstraints()) {
|
|
87
|
+
for (const table of toSchema.getTables()) {
|
|
88
|
+
const fks = Object.values(table.getForeignKeys()).map(fk => this.helper.createForeignKey(table, fk));
|
|
89
|
+
this.append(ret, fks, true);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Create views after tables (views may depend on tables)
|
|
93
|
+
// Sort views by dependencies (views depending on other views come later)
|
|
94
|
+
const sortedViews = this.sortViewsByDependencies(toSchema.getViews());
|
|
95
|
+
for (const view of sortedViews) {
|
|
96
|
+
if (view.materialized) {
|
|
97
|
+
this.append(ret, this.helper.createMaterializedView(view.name, view.schema, view.definition, view.withData ?? true));
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
this.append(ret, this.helper.createView(view.name, view.schema, view.definition), true);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return this.wrapSchema(ret, options);
|
|
280
104
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
);
|
|
288
|
-
this.append(ret, sql);
|
|
289
|
-
}
|
|
105
|
+
async drop(options = {}) {
|
|
106
|
+
if (options.dropDb) {
|
|
107
|
+
const name = this.config.get('dbName');
|
|
108
|
+
return this.dropDatabase(name);
|
|
109
|
+
}
|
|
110
|
+
const sql = await this.getDropSchemaSQL(options);
|
|
111
|
+
await this.execute(sql);
|
|
290
112
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const sortedRemovedViews = this.sortViewsByDependencies(Object.values(schemaDiff.removedViews)).reverse();
|
|
295
|
-
for (const view of sortedRemovedViews) {
|
|
296
|
-
if (view.materialized) {
|
|
297
|
-
this.append(ret, this.helper.dropMaterializedViewIfExists(view.name, view.schema));
|
|
298
|
-
} else {
|
|
299
|
-
this.append(ret, this.helper.dropViewIfExists(view.name, view.schema));
|
|
300
|
-
}
|
|
301
|
-
}
|
|
113
|
+
async createNamespace(name) {
|
|
114
|
+
const sql = this.helper.getCreateNamespaceSQL(name);
|
|
115
|
+
await this.execute(sql);
|
|
302
116
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const sortedChangedViewsFrom = this.sortViewsByDependencies(changedViewsFrom).reverse();
|
|
307
|
-
for (const view of sortedChangedViewsFrom) {
|
|
308
|
-
if (view.materialized) {
|
|
309
|
-
this.append(ret, this.helper.dropMaterializedViewIfExists(view.name, view.schema));
|
|
310
|
-
} else {
|
|
311
|
-
this.append(ret, this.helper.dropViewIfExists(view.name, view.schema));
|
|
312
|
-
}
|
|
117
|
+
async dropNamespace(name) {
|
|
118
|
+
const sql = this.helper.getDropNamespaceSQL(name);
|
|
119
|
+
await this.execute(sql);
|
|
313
120
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const [schemaName, tableName] = this.helper.splitTableName(orphanedForeignKey.localTableName, true);
|
|
121
|
+
async clear(options) {
|
|
122
|
+
// truncate by default, so no value is considered as true
|
|
317
123
|
/* v8 ignore next */
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
ret.push('');
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
for (const changedTable of Object.values(schemaDiff.changedTables)) {
|
|
350
|
-
this.append(ret, this.preAlterTable(changedTable, options.safe), true);
|
|
351
|
-
}
|
|
352
|
-
for (const changedTable of Object.values(schemaDiff.changedTables)) {
|
|
353
|
-
this.append(ret, this.helper.alterTable(changedTable, options.safe), true);
|
|
354
|
-
}
|
|
355
|
-
for (const changedTable of Object.values(schemaDiff.changedTables)) {
|
|
356
|
-
this.append(ret, this.helper.getPostAlterTable(changedTable, options.safe), true);
|
|
357
|
-
}
|
|
358
|
-
if (!options.safe && this.platform.supportsNativeEnums()) {
|
|
359
|
-
for (const removedNativeEnum of schemaDiff.removedNativeEnums) {
|
|
360
|
-
this.append(ret, this.helper.getDropNativeEnumSQL(removedNativeEnum.name, removedNativeEnum.schema));
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
if (options.dropTables && !options.safe) {
|
|
364
|
-
for (const removedNamespace of schemaDiff.removedNamespaces) {
|
|
365
|
-
const sql = this.helper.getDropNamespaceSQL(removedNamespace);
|
|
366
|
-
this.append(ret, sql);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
// Create new views after all table changes are done
|
|
370
|
-
// Sort views by dependencies (views depending on other views come later)
|
|
371
|
-
const sortedNewViews = this.sortViewsByDependencies(Object.values(schemaDiff.newViews));
|
|
372
|
-
for (const view of sortedNewViews) {
|
|
373
|
-
if (view.materialized) {
|
|
374
|
-
this.append(
|
|
375
|
-
ret,
|
|
376
|
-
this.helper.createMaterializedView(view.name, view.schema, view.definition, view.withData ?? true),
|
|
377
|
-
);
|
|
378
|
-
} else {
|
|
379
|
-
this.append(ret, this.helper.createView(view.name, view.schema, view.definition), true);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
// Recreate changed views (also sorted by dependencies)
|
|
383
|
-
const changedViews = Object.values(schemaDiff.changedViews).map(v => v.to);
|
|
384
|
-
const sortedChangedViews = this.sortViewsByDependencies(changedViews);
|
|
385
|
-
for (const view of sortedChangedViews) {
|
|
386
|
-
if (view.materialized) {
|
|
387
|
-
this.append(
|
|
388
|
-
ret,
|
|
389
|
-
this.helper.createMaterializedView(view.name, view.schema, view.definition, view.withData ?? true),
|
|
390
|
-
);
|
|
391
|
-
} else {
|
|
392
|
-
this.append(ret, this.helper.createView(view.name, view.schema, view.definition), true);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
return this.wrapSchema(ret, options);
|
|
396
|
-
}
|
|
397
|
-
/**
|
|
398
|
-
* We need to drop foreign keys first for all tables to allow dropping PK constraints.
|
|
399
|
-
*/
|
|
400
|
-
preAlterTable(diff, safe) {
|
|
401
|
-
const ret = [];
|
|
402
|
-
this.append(ret, this.helper.getPreAlterTable(diff, safe));
|
|
403
|
-
for (const foreignKey of Object.values(diff.removedForeignKeys)) {
|
|
404
|
-
ret.push(this.helper.dropForeignKey(diff.toTable.getShortestName(), foreignKey.constraintName));
|
|
405
|
-
}
|
|
406
|
-
for (const foreignKey of Object.values(diff.changedForeignKeys)) {
|
|
407
|
-
ret.push(this.helper.dropForeignKey(diff.toTable.getShortestName(), foreignKey.constraintName));
|
|
408
|
-
}
|
|
409
|
-
return ret;
|
|
410
|
-
}
|
|
411
|
-
/**
|
|
412
|
-
* creates new database and connects to it
|
|
413
|
-
*/
|
|
414
|
-
async createDatabase(name, options) {
|
|
415
|
-
name ??= this.config.get('dbName');
|
|
416
|
-
const sql = this.helper.getCreateDatabaseSQL(name);
|
|
417
|
-
if (sql) {
|
|
418
|
-
await this.execute(sql);
|
|
419
|
-
}
|
|
420
|
-
this.config.set('dbName', name);
|
|
421
|
-
await this.driver.reconnect(options);
|
|
422
|
-
}
|
|
423
|
-
async dropDatabase(name) {
|
|
424
|
-
name ??= this.config.get('dbName');
|
|
425
|
-
this.config.set('dbName', this.helper.getManagementDbName());
|
|
426
|
-
await this.driver.reconnect();
|
|
427
|
-
await this.execute(this.helper.getDropDatabaseSQL(name));
|
|
428
|
-
this.config.set('dbName', name);
|
|
429
|
-
}
|
|
430
|
-
async execute(sql, options = {}) {
|
|
431
|
-
options.wrap ??= false;
|
|
432
|
-
const lines = this.wrapSchema(sql, options).split('\n');
|
|
433
|
-
const groups = [];
|
|
434
|
-
let i = 0;
|
|
435
|
-
for (const line of lines) {
|
|
436
|
-
if (line.trim() === '') {
|
|
437
|
-
if (groups[i]?.length > 0) {
|
|
438
|
-
i++;
|
|
439
|
-
}
|
|
440
|
-
continue;
|
|
441
|
-
}
|
|
442
|
-
groups[i] ??= [];
|
|
443
|
-
groups[i].push(line.trim());
|
|
444
|
-
}
|
|
445
|
-
if (groups.length === 0) {
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
if (this.platform.supportsMultipleStatements()) {
|
|
449
|
-
for (const group of groups) {
|
|
450
|
-
const query = group.join('\n');
|
|
451
|
-
await this.driver.execute(query);
|
|
452
|
-
}
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
const statements = groups.flatMap(group => {
|
|
456
|
-
return group
|
|
457
|
-
.join('\n')
|
|
458
|
-
.split(';\n')
|
|
459
|
-
.map(s => s.trim())
|
|
460
|
-
.filter(s => s);
|
|
461
|
-
});
|
|
462
|
-
await Utils.runSerial(statements, stmt => this.driver.execute(stmt));
|
|
463
|
-
}
|
|
464
|
-
async dropTableIfExists(name, schema) {
|
|
465
|
-
const sql = this.helper.dropTableIfExists(name, schema);
|
|
466
|
-
return this.execute(sql);
|
|
467
|
-
}
|
|
468
|
-
wrapSchema(sql, options) {
|
|
469
|
-
const array = Utils.asArray(sql);
|
|
470
|
-
if (array.length === 0) {
|
|
471
|
-
return '';
|
|
472
|
-
}
|
|
473
|
-
if (array[array.length - 1] === '') {
|
|
474
|
-
array.pop();
|
|
475
|
-
}
|
|
476
|
-
if (options.wrap === false) {
|
|
477
|
-
return array.join('\n') + '\n';
|
|
478
|
-
}
|
|
479
|
-
let ret = this.helper.getSchemaBeginning(this.config.get('charset'), this.options.disableForeignKeys);
|
|
480
|
-
ret += array.join('\n') + '\n';
|
|
481
|
-
ret += this.helper.getSchemaEnd(this.options.disableForeignKeys);
|
|
482
|
-
return ret;
|
|
483
|
-
}
|
|
484
|
-
append(array, sql, pad) {
|
|
485
|
-
return this.helper.append(array, sql, pad);
|
|
486
|
-
}
|
|
487
|
-
matchName(name, nameToMatch) {
|
|
488
|
-
return typeof nameToMatch === 'string'
|
|
489
|
-
? name.toLocaleLowerCase() === nameToMatch.toLocaleLowerCase()
|
|
490
|
-
: nameToMatch.test(name);
|
|
491
|
-
}
|
|
492
|
-
isTableSkipped(tableName, schemaName) {
|
|
493
|
-
const skipTables = this.options.skipTables;
|
|
494
|
-
if (!skipTables || skipTables.length === 0) {
|
|
495
|
-
return false;
|
|
124
|
+
if (options?.truncate === false) {
|
|
125
|
+
return super.clear(options);
|
|
126
|
+
}
|
|
127
|
+
if (this.options.disableForeignKeysForClear) {
|
|
128
|
+
await this.execute(this.helper.disableForeignKeysSQL());
|
|
129
|
+
}
|
|
130
|
+
const schema = options?.schema ?? this.config.get('schema', this.platform.getDefaultSchemaName());
|
|
131
|
+
for (const meta of this.getOrderedMetadata(schema).reverse()) {
|
|
132
|
+
try {
|
|
133
|
+
await this.driver
|
|
134
|
+
.createQueryBuilder(meta.class, this.em?.getTransactionContext(), 'write', false)
|
|
135
|
+
.withSchema(schema)
|
|
136
|
+
.truncate()
|
|
137
|
+
.execute();
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
if (this.platform.getExceptionConverter().convertException(e) instanceof TableNotFoundException) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
throw e;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (this.options.disableForeignKeysForClear) {
|
|
147
|
+
await this.execute(this.helper.enableForeignKeysSQL());
|
|
148
|
+
}
|
|
149
|
+
if (options?.clearIdentityMap ?? true) {
|
|
150
|
+
this.clearIdentityMap();
|
|
151
|
+
}
|
|
496
152
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
153
|
+
async getDropSchemaSQL(options = {}) {
|
|
154
|
+
await this.ensureDatabase();
|
|
155
|
+
const metadata = this.getOrderedMetadata(options.schema).reverse();
|
|
156
|
+
const schemas = this.getTargetSchema(options.schema).getNamespaces();
|
|
157
|
+
const schema = await DatabaseSchema.create(this.connection, this.platform, this.config, options.schema, schemas, undefined, this.options.skipTables, this.options.skipViews);
|
|
158
|
+
const ret = [];
|
|
159
|
+
// Drop views first (views may depend on tables)
|
|
160
|
+
// Drop in reverse dependency order (dependent views first)
|
|
161
|
+
const targetSchema = this.getTargetSchema(options.schema);
|
|
162
|
+
const sortedViews = this.sortViewsByDependencies(targetSchema.getViews()).reverse();
|
|
163
|
+
for (const view of sortedViews) {
|
|
164
|
+
if (view.materialized) {
|
|
165
|
+
this.append(ret, this.helper.dropMaterializedViewIfExists(view.name, view.schema));
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
this.append(ret, this.helper.dropViewIfExists(view.name, view.schema));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// remove FKs explicitly if we can't use a cascading statement and we don't disable FK checks (we need this for circular relations)
|
|
172
|
+
for (const meta of metadata) {
|
|
173
|
+
if (!this.platform.usesCascadeStatement() && (!this.options.disableForeignKeys || options.dropForeignKeys)) {
|
|
174
|
+
const table = schema.getTable(meta.tableName);
|
|
175
|
+
if (!table) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const foreignKeys = Object.values(table.getForeignKeys());
|
|
179
|
+
for (const fk of foreignKeys) {
|
|
180
|
+
this.append(ret, this.helper.dropForeignKey(table.getShortestName(), fk.constraintName));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
for (const meta of metadata) {
|
|
185
|
+
this.append(ret, this.helper.dropTableIfExists(meta.tableName, this.getSchemaName(meta, options)));
|
|
186
|
+
}
|
|
187
|
+
if (this.platform.supportsNativeEnums()) {
|
|
188
|
+
for (const columnName of Object.keys(schema.getNativeEnums())) {
|
|
189
|
+
const sql = this.helper.getDropNativeEnumSQL(columnName, options.schema ?? this.config.get('schema'));
|
|
190
|
+
this.append(ret, sql);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (options.dropMigrationsTable) {
|
|
194
|
+
this.append(ret, this.helper.dropTableIfExists(this.config.get('migrations').tableName, this.config.get('schema')));
|
|
195
|
+
}
|
|
196
|
+
return this.wrapSchema(ret, options);
|
|
507
197
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
198
|
+
getSchemaName(meta, options) {
|
|
199
|
+
const schemaName = options.schema ?? this.config.get('schema');
|
|
200
|
+
/* v8 ignore next */
|
|
201
|
+
const resolvedName = meta.schema && meta.schema === '*' ? schemaName : (meta.schema ?? schemaName);
|
|
202
|
+
// skip default schema name
|
|
203
|
+
if (resolvedName === this.platform.getDefaultSchemaName()) {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
return resolvedName;
|
|
207
|
+
}
|
|
208
|
+
async update(options = {}) {
|
|
209
|
+
const sql = await this.getUpdateSchemaSQL(options);
|
|
210
|
+
await this.execute(sql);
|
|
211
|
+
}
|
|
212
|
+
async getUpdateSchemaSQL(options = {}) {
|
|
213
|
+
await this.ensureDatabase();
|
|
214
|
+
const { fromSchema, toSchema } = await this.prepareSchemaForComparison(options);
|
|
215
|
+
const comparator = new SchemaComparator(this.platform);
|
|
216
|
+
const diffUp = comparator.compare(fromSchema, toSchema);
|
|
217
|
+
return this.diffToSQL(diffUp, options);
|
|
218
|
+
}
|
|
219
|
+
async getUpdateSchemaMigrationSQL(options = {}) {
|
|
220
|
+
if (!options.fromSchema) {
|
|
221
|
+
await this.ensureDatabase();
|
|
222
|
+
}
|
|
223
|
+
const { fromSchema, toSchema } = await this.prepareSchemaForComparison(options);
|
|
224
|
+
const comparator = new SchemaComparator(this.platform);
|
|
225
|
+
const diffUp = comparator.compare(fromSchema, toSchema);
|
|
226
|
+
const diffDown = comparator.compare(toSchema, fromSchema, diffUp);
|
|
227
|
+
return {
|
|
228
|
+
up: this.diffToSQL(diffUp, options),
|
|
229
|
+
down: this.platform.supportsDownMigrations() ? this.diffToSQL(diffDown, options) : '',
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
async prepareSchemaForComparison(options) {
|
|
233
|
+
options.safe ??= false;
|
|
234
|
+
options.dropTables ??= true;
|
|
235
|
+
const toSchema = this.getTargetSchema(options.schema);
|
|
236
|
+
const schemas = toSchema.getNamespaces();
|
|
237
|
+
const fromSchema = options.fromSchema ??
|
|
238
|
+
(await DatabaseSchema.create(this.connection, this.platform, this.config, options.schema, schemas, undefined, this.options.skipTables, this.options.skipViews));
|
|
239
|
+
const wildcardSchemaTables = [...this.metadata.getAll().values()]
|
|
240
|
+
.filter(meta => meta.schema === '*')
|
|
241
|
+
.map(meta => meta.tableName);
|
|
242
|
+
fromSchema.prune(options.schema, wildcardSchemaTables);
|
|
243
|
+
toSchema.prune(options.schema, wildcardSchemaTables);
|
|
244
|
+
return { fromSchema, toSchema };
|
|
245
|
+
}
|
|
246
|
+
diffToSQL(schemaDiff, options) {
|
|
247
|
+
const ret = [];
|
|
248
|
+
globalThis.idx = 0;
|
|
249
|
+
if (this.platform.supportsSchemas()) {
|
|
250
|
+
for (const newNamespace of schemaDiff.newNamespaces) {
|
|
251
|
+
const sql = this.helper.getCreateNamespaceSQL(newNamespace);
|
|
252
|
+
this.append(ret, sql);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (this.platform.supportsNativeEnums()) {
|
|
256
|
+
for (const newNativeEnum of schemaDiff.newNativeEnums) {
|
|
257
|
+
const sql = this.helper.getCreateNativeEnumSQL(newNativeEnum.name, newNativeEnum.items, this.getSchemaName(newNativeEnum, options));
|
|
258
|
+
this.append(ret, sql);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// Drop removed and changed views first (before modifying tables they may depend on)
|
|
262
|
+
// Drop in reverse dependency order (dependent views first)
|
|
263
|
+
if (options.dropTables && !options.safe) {
|
|
264
|
+
const sortedRemovedViews = this.sortViewsByDependencies(Object.values(schemaDiff.removedViews)).reverse();
|
|
265
|
+
for (const view of sortedRemovedViews) {
|
|
266
|
+
if (view.materialized) {
|
|
267
|
+
this.append(ret, this.helper.dropMaterializedViewIfExists(view.name, view.schema));
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
this.append(ret, this.helper.dropViewIfExists(view.name, view.schema));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
// Drop changed views (they will be recreated after table changes)
|
|
275
|
+
// Also in reverse dependency order
|
|
276
|
+
const changedViewsFrom = Object.values(schemaDiff.changedViews).map(v => v.from);
|
|
277
|
+
const sortedChangedViewsFrom = this.sortViewsByDependencies(changedViewsFrom).reverse();
|
|
278
|
+
for (const view of sortedChangedViewsFrom) {
|
|
279
|
+
if (view.materialized) {
|
|
280
|
+
this.append(ret, this.helper.dropMaterializedViewIfExists(view.name, view.schema));
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
this.append(ret, this.helper.dropViewIfExists(view.name, view.schema));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (!options.safe && this.options.createForeignKeyConstraints) {
|
|
287
|
+
for (const orphanedForeignKey of schemaDiff.orphanedForeignKeys) {
|
|
288
|
+
const [schemaName, tableName] = this.helper.splitTableName(orphanedForeignKey.localTableName, true);
|
|
289
|
+
/* v8 ignore next */
|
|
290
|
+
const name = (schemaName ? schemaName + '.' : '') + tableName;
|
|
291
|
+
this.append(ret, this.helper.dropForeignKey(name, orphanedForeignKey.constraintName));
|
|
292
|
+
}
|
|
293
|
+
if (schemaDiff.orphanedForeignKeys.length > 0) {
|
|
294
|
+
ret.push('');
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
for (const newTable of Object.values(schemaDiff.newTables)) {
|
|
298
|
+
this.append(ret, this.helper.createTable(newTable, true), true);
|
|
299
|
+
}
|
|
300
|
+
if (this.helper.supportsSchemaConstraints()) {
|
|
301
|
+
for (const newTable of Object.values(schemaDiff.newTables)) {
|
|
302
|
+
const sql = [];
|
|
303
|
+
if (this.options.createForeignKeyConstraints) {
|
|
304
|
+
const fks = Object.values(newTable.getForeignKeys()).map(fk => this.helper.createForeignKey(newTable, fk));
|
|
305
|
+
this.append(sql, fks);
|
|
306
|
+
}
|
|
307
|
+
for (const check of newTable.getChecks()) {
|
|
308
|
+
this.append(sql, this.helper.createCheck(newTable, check));
|
|
309
|
+
}
|
|
310
|
+
this.append(ret, sql, true);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (options.dropTables && !options.safe) {
|
|
314
|
+
for (const table of Object.values(schemaDiff.removedTables)) {
|
|
315
|
+
this.append(ret, this.helper.dropTableIfExists(table.name, table.schema));
|
|
316
|
+
}
|
|
317
|
+
if (Utils.hasObjectKeys(schemaDiff.removedTables)) {
|
|
318
|
+
ret.push('');
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
for (const changedTable of Object.values(schemaDiff.changedTables)) {
|
|
322
|
+
this.append(ret, this.preAlterTable(changedTable, options.safe), true);
|
|
323
|
+
}
|
|
324
|
+
for (const changedTable of Object.values(schemaDiff.changedTables)) {
|
|
325
|
+
this.append(ret, this.helper.alterTable(changedTable, options.safe), true);
|
|
326
|
+
}
|
|
327
|
+
for (const changedTable of Object.values(schemaDiff.changedTables)) {
|
|
328
|
+
this.append(ret, this.helper.getPostAlterTable(changedTable, options.safe), true);
|
|
329
|
+
}
|
|
330
|
+
if (!options.safe && this.platform.supportsNativeEnums()) {
|
|
331
|
+
for (const removedNativeEnum of schemaDiff.removedNativeEnums) {
|
|
332
|
+
this.append(ret, this.helper.getDropNativeEnumSQL(removedNativeEnum.name, removedNativeEnum.schema));
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (options.dropTables && !options.safe) {
|
|
336
|
+
for (const removedNamespace of schemaDiff.removedNamespaces) {
|
|
337
|
+
const sql = this.helper.getDropNamespaceSQL(removedNamespace);
|
|
338
|
+
this.append(ret, sql);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// Create new views after all table changes are done
|
|
342
|
+
// Sort views by dependencies (views depending on other views come later)
|
|
343
|
+
const sortedNewViews = this.sortViewsByDependencies(Object.values(schemaDiff.newViews));
|
|
344
|
+
for (const view of sortedNewViews) {
|
|
345
|
+
if (view.materialized) {
|
|
346
|
+
this.append(ret, this.helper.createMaterializedView(view.name, view.schema, view.definition, view.withData ?? true));
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
this.append(ret, this.helper.createView(view.name, view.schema, view.definition), true);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
// Recreate changed views (also sorted by dependencies)
|
|
353
|
+
const changedViews = Object.values(schemaDiff.changedViews).map(v => v.to);
|
|
354
|
+
const sortedChangedViews = this.sortViewsByDependencies(changedViews);
|
|
355
|
+
for (const view of sortedChangedViews) {
|
|
356
|
+
if (view.materialized) {
|
|
357
|
+
this.append(ret, this.helper.createMaterializedView(view.name, view.schema, view.definition, view.withData ?? true));
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
this.append(ret, this.helper.createView(view.name, view.schema, view.definition), true);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return this.wrapSchema(ret, options);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* We need to drop foreign keys first for all tables to allow dropping PK constraints.
|
|
367
|
+
*/
|
|
368
|
+
preAlterTable(diff, safe) {
|
|
369
|
+
const ret = [];
|
|
370
|
+
this.append(ret, this.helper.getPreAlterTable(diff, safe));
|
|
371
|
+
for (const foreignKey of Object.values(diff.removedForeignKeys)) {
|
|
372
|
+
ret.push(this.helper.dropForeignKey(diff.toTable.getShortestName(), foreignKey.constraintName));
|
|
373
|
+
}
|
|
374
|
+
for (const foreignKey of Object.values(diff.changedForeignKeys)) {
|
|
375
|
+
ret.push(this.helper.dropForeignKey(diff.toTable.getShortestName(), foreignKey.constraintName));
|
|
376
|
+
}
|
|
377
|
+
return ret;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* creates new database and connects to it
|
|
381
|
+
*/
|
|
382
|
+
async createDatabase(name, options) {
|
|
383
|
+
name ??= this.config.get('dbName');
|
|
384
|
+
const sql = this.helper.getCreateDatabaseSQL(name);
|
|
385
|
+
if (sql) {
|
|
386
|
+
await this.execute(sql);
|
|
387
|
+
}
|
|
388
|
+
this.config.set('dbName', name);
|
|
389
|
+
await this.driver.reconnect(options);
|
|
390
|
+
}
|
|
391
|
+
async dropDatabase(name) {
|
|
392
|
+
name ??= this.config.get('dbName');
|
|
393
|
+
this.config.set('dbName', this.helper.getManagementDbName());
|
|
394
|
+
await this.driver.reconnect();
|
|
395
|
+
await this.execute(this.helper.getDropDatabaseSQL(name));
|
|
396
|
+
this.config.set('dbName', name);
|
|
397
|
+
}
|
|
398
|
+
async execute(sql, options = {}) {
|
|
399
|
+
options.wrap ??= false;
|
|
400
|
+
const lines = this.wrapSchema(sql, options).split('\n');
|
|
401
|
+
const groups = [];
|
|
402
|
+
let i = 0;
|
|
403
|
+
for (const line of lines) {
|
|
404
|
+
if (line.trim() === '') {
|
|
405
|
+
if (groups[i]?.length > 0) {
|
|
406
|
+
i++;
|
|
407
|
+
}
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
groups[i] ??= [];
|
|
411
|
+
groups[i].push(line.trim());
|
|
412
|
+
}
|
|
413
|
+
if (groups.length === 0) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (this.platform.supportsMultipleStatements()) {
|
|
417
|
+
for (const group of groups) {
|
|
418
|
+
const query = group.join('\n');
|
|
419
|
+
await this.driver.execute(query);
|
|
420
|
+
}
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const statements = groups.flatMap(group => {
|
|
424
|
+
return group
|
|
425
|
+
.join('\n')
|
|
426
|
+
.split(';\n')
|
|
427
|
+
.map(s => s.trim())
|
|
428
|
+
.filter(s => s);
|
|
429
|
+
});
|
|
430
|
+
await Utils.runSerial(statements, stmt => this.driver.execute(stmt));
|
|
431
|
+
}
|
|
432
|
+
async dropTableIfExists(name, schema) {
|
|
433
|
+
const sql = this.helper.dropTableIfExists(name, schema);
|
|
434
|
+
return this.execute(sql);
|
|
435
|
+
}
|
|
436
|
+
wrapSchema(sql, options) {
|
|
437
|
+
const array = Utils.asArray(sql);
|
|
438
|
+
if (array.length === 0) {
|
|
439
|
+
return '';
|
|
440
|
+
}
|
|
441
|
+
if (array[array.length - 1] === '') {
|
|
442
|
+
array.pop();
|
|
443
|
+
}
|
|
444
|
+
if (options.wrap === false) {
|
|
445
|
+
return array.join('\n') + '\n';
|
|
446
|
+
}
|
|
447
|
+
let ret = this.helper.getSchemaBeginning(this.config.get('charset'), this.options.disableForeignKeys);
|
|
448
|
+
ret += array.join('\n') + '\n';
|
|
449
|
+
ret += this.helper.getSchemaEnd(this.options.disableForeignKeys);
|
|
450
|
+
return ret;
|
|
451
|
+
}
|
|
452
|
+
append(array, sql, pad) {
|
|
453
|
+
return this.helper.append(array, sql, pad);
|
|
454
|
+
}
|
|
455
|
+
matchName(name, nameToMatch) {
|
|
456
|
+
return typeof nameToMatch === 'string'
|
|
457
|
+
? name.toLocaleLowerCase() === nameToMatch.toLocaleLowerCase()
|
|
458
|
+
: nameToMatch.test(name);
|
|
459
|
+
}
|
|
460
|
+
isTableSkipped(tableName, schemaName) {
|
|
461
|
+
const skipTables = this.options.skipTables;
|
|
462
|
+
if (!skipTables || skipTables.length === 0) {
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
const fullTableName = schemaName ? `${schemaName}.${tableName}` : tableName;
|
|
466
|
+
return skipTables.some(pattern => this.matchName(tableName, pattern) || this.matchName(fullTableName, pattern));
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Sorts views by their dependencies so that views depending on other views are created after their dependencies.
|
|
470
|
+
* Uses topological sort based on view definition string matching.
|
|
471
|
+
*/
|
|
472
|
+
sortViewsByDependencies(views) {
|
|
473
|
+
if (views.length <= 1) {
|
|
474
|
+
return views;
|
|
475
|
+
}
|
|
476
|
+
// Use CommitOrderCalculator for topological sort
|
|
477
|
+
const calc = new CommitOrderCalculator();
|
|
478
|
+
// Map views to numeric indices for the calculator
|
|
479
|
+
const viewToIndex = new Map();
|
|
480
|
+
const indexToView = new Map();
|
|
481
|
+
for (let i = 0; i < views.length; i++) {
|
|
482
|
+
viewToIndex.set(views[i], i);
|
|
483
|
+
indexToView.set(i, views[i]);
|
|
484
|
+
calc.addNode(i);
|
|
485
|
+
}
|
|
486
|
+
// Check each view's definition for references to other view names
|
|
487
|
+
for (const view of views) {
|
|
488
|
+
const definition = view.definition.toLowerCase();
|
|
489
|
+
const viewIndex = viewToIndex.get(view);
|
|
490
|
+
for (const otherView of views) {
|
|
491
|
+
if (otherView === view) {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
// Check if the definition references the other view's name
|
|
495
|
+
// Use word boundary matching to avoid false positives
|
|
496
|
+
const patterns = [new RegExp(`\\b${this.escapeRegExp(otherView.name.toLowerCase())}\\b`)];
|
|
497
|
+
if (otherView.schema) {
|
|
498
|
+
patterns.push(new RegExp(`\\b${this.escapeRegExp(`${otherView.schema}.${otherView.name}`.toLowerCase())}\\b`));
|
|
499
|
+
}
|
|
500
|
+
for (const pattern of patterns) {
|
|
501
|
+
if (pattern.test(definition)) {
|
|
502
|
+
// view depends on otherView, so otherView must come first
|
|
503
|
+
// addDependency(from, to) puts `from` before `to` in result
|
|
504
|
+
const otherIndex = viewToIndex.get(otherView);
|
|
505
|
+
calc.addDependency(otherIndex, viewIndex, 1);
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
// Sort and map back to views
|
|
512
|
+
return calc.sort().map(index => indexToView.get(index));
|
|
517
513
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
const definition = view.definition.toLowerCase();
|
|
521
|
-
const viewIndex = viewToIndex.get(view);
|
|
522
|
-
for (const otherView of views) {
|
|
523
|
-
if (otherView === view) {
|
|
524
|
-
continue;
|
|
525
|
-
}
|
|
526
|
-
// Check if the definition references the other view's name
|
|
527
|
-
// Use word boundary matching to avoid false positives
|
|
528
|
-
const patterns = [new RegExp(`\\b${this.escapeRegExp(otherView.name.toLowerCase())}\\b`)];
|
|
529
|
-
if (otherView.schema) {
|
|
530
|
-
patterns.push(
|
|
531
|
-
new RegExp(`\\b${this.escapeRegExp(`${otherView.schema}.${otherView.name}`.toLowerCase())}\\b`),
|
|
532
|
-
);
|
|
533
|
-
}
|
|
534
|
-
for (const pattern of patterns) {
|
|
535
|
-
if (pattern.test(definition)) {
|
|
536
|
-
// view depends on otherView, so otherView must come first
|
|
537
|
-
// addDependency(from, to) puts `from` before `to` in result
|
|
538
|
-
const otherIndex = viewToIndex.get(otherView);
|
|
539
|
-
calc.addDependency(otherIndex, viewIndex, 1);
|
|
540
|
-
break;
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
}
|
|
514
|
+
escapeRegExp(string) {
|
|
515
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
544
516
|
}
|
|
545
|
-
// Sort and map back to views
|
|
546
|
-
return calc.sort().map(index => indexToView.get(index));
|
|
547
|
-
}
|
|
548
|
-
escapeRegExp(string) {
|
|
549
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
550
|
-
}
|
|
551
517
|
}
|
|
552
518
|
// for back compatibility
|
|
553
519
|
export { SqlSchemaGenerator as SchemaGenerator };
|