@mikro-orm/sql 7.0.2-dev.9 → 7.0.2
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 +95 -47
- package/AbstractSqlConnection.js +240 -232
- package/AbstractSqlDriver.d.ts +412 -155
- package/AbstractSqlDriver.js +2062 -1937
- package/AbstractSqlPlatform.d.ts +84 -73
- package/AbstractSqlPlatform.js +163 -158
- package/PivotCollectionPersister.d.ts +33 -15
- package/PivotCollectionPersister.js +158 -160
- package/README.md +128 -294
- package/SqlEntityManager.d.ts +68 -20
- package/SqlEntityManager.js +54 -37
- package/SqlEntityRepository.d.ts +15 -14
- package/SqlEntityRepository.js +24 -23
- package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +12 -12
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +192 -194
- package/dialects/mysql/BaseMySqlPlatform.d.ts +64 -45
- package/dialects/mysql/BaseMySqlPlatform.js +134 -131
- package/dialects/mysql/MySqlExceptionConverter.d.ts +6 -6
- package/dialects/mysql/MySqlExceptionConverter.js +91 -77
- package/dialects/mysql/MySqlNativeQueryBuilder.d.ts +3 -3
- package/dialects/mysql/MySqlNativeQueryBuilder.js +66 -69
- package/dialects/mysql/MySqlSchemaHelper.d.ts +39 -39
- package/dialects/mysql/MySqlSchemaHelper.js +327 -319
- package/dialects/oracledb/OracleDialect.d.ts +81 -52
- package/dialects/oracledb/OracleDialect.js +155 -149
- package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +12 -12
- package/dialects/oracledb/OracleNativeQueryBuilder.js +232 -236
- package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +108 -105
- package/dialects/postgresql/BasePostgreSqlPlatform.js +351 -350
- package/dialects/postgresql/FullTextType.d.ts +10 -6
- package/dialects/postgresql/FullTextType.js +51 -51
- package/dialects/postgresql/PostgreSqlExceptionConverter.d.ts +5 -5
- package/dialects/postgresql/PostgreSqlExceptionConverter.js +55 -43
- package/dialects/postgresql/PostgreSqlNativeQueryBuilder.d.ts +1 -1
- package/dialects/postgresql/PostgreSqlNativeQueryBuilder.js +4 -4
- package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +102 -82
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +711 -683
- package/dialects/sqlite/BaseSqliteConnection.d.ts +3 -5
- package/dialects/sqlite/BaseSqliteConnection.js +21 -19
- 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 +67 -51
- package/dialects/sqlite/SqliteNativeQueryBuilder.d.ts +2 -2
- package/dialects/sqlite/SqliteNativeQueryBuilder.js +7 -7
- package/dialects/sqlite/SqlitePlatform.d.ts +63 -72
- package/dialects/sqlite/SqlitePlatform.js +139 -139
- package/dialects/sqlite/SqliteSchemaHelper.d.ts +70 -60
- package/dialects/sqlite/SqliteSchemaHelper.js +533 -520
- package/package.json +4 -4
- package/plugin/index.d.ts +44 -35
- package/plugin/index.js +44 -36
- package/plugin/transformer.d.ts +117 -94
- package/plugin/transformer.js +890 -881
- package/query/ArrayCriteriaNode.d.ts +4 -4
- package/query/ArrayCriteriaNode.js +18 -18
- package/query/CriteriaNode.d.ts +35 -25
- package/query/CriteriaNode.js +133 -123
- package/query/CriteriaNodeFactory.d.ts +49 -6
- package/query/CriteriaNodeFactory.js +97 -94
- package/query/NativeQueryBuilder.d.ts +120 -117
- package/query/NativeQueryBuilder.js +484 -480
- package/query/ObjectCriteriaNode.d.ts +12 -12
- package/query/ObjectCriteriaNode.js +298 -282
- package/query/QueryBuilder.d.ts +1546 -904
- package/query/QueryBuilder.js +2270 -2145
- package/query/QueryBuilderHelper.d.ts +153 -72
- package/query/QueryBuilderHelper.js +1079 -1028
- package/query/ScalarCriteriaNode.d.ts +3 -3
- package/query/ScalarCriteriaNode.js +53 -46
- package/query/enums.d.ts +16 -14
- package/query/enums.js +16 -14
- package/query/raw.d.ts +16 -6
- package/query/raw.js +10 -10
- package/schema/DatabaseSchema.d.ts +73 -50
- package/schema/DatabaseSchema.js +331 -307
- package/schema/DatabaseTable.d.ts +96 -73
- package/schema/DatabaseTable.js +1012 -927
- package/schema/SchemaComparator.d.ts +58 -54
- package/schema/SchemaComparator.js +745 -719
- package/schema/SchemaHelper.d.ts +110 -80
- package/schema/SchemaHelper.js +676 -645
- package/schema/SqlSchemaGenerator.d.ts +79 -58
- package/schema/SqlSchemaGenerator.js +536 -501
- package/typings.d.ts +380 -266
package/schema/SchemaHelper.js
CHANGED
|
@@ -1,653 +1,684 @@
|
|
|
1
1
|
import { RawQueryFragment, Utils } from '@mikro-orm/core';
|
|
2
|
+
/** Base class for database-specific schema helpers. Provides SQL generation for DDL operations. */
|
|
2
3
|
export class SchemaHelper {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
getRenameIndexSQL(tableName, index, oldIndexName) {
|
|
152
|
-
return [
|
|
153
|
-
this.getDropIndexSQL(tableName, { ...index, keyName: oldIndexName }),
|
|
154
|
-
this.getCreateIndexSQL(tableName, index),
|
|
155
|
-
];
|
|
156
|
-
}
|
|
157
|
-
alterTable(diff, safe) {
|
|
158
|
-
const ret = [];
|
|
159
|
-
const [schemaName, tableName] = this.splitTableName(diff.name);
|
|
160
|
-
if (this.platform.supportsNativeEnums()) {
|
|
161
|
-
const changedNativeEnums = [];
|
|
162
|
-
for (const { column, changedProperties } of Object.values(diff.changedColumns)) {
|
|
163
|
-
if (!column.nativeEnumName) {
|
|
164
|
-
continue;
|
|
165
|
-
}
|
|
166
|
-
const key = schemaName && schemaName !== this.platform.getDefaultSchemaName() && !column.nativeEnumName.includes('.')
|
|
167
|
-
? schemaName + '.' + column.nativeEnumName
|
|
168
|
-
: column.nativeEnumName;
|
|
169
|
-
if (changedProperties.has('enumItems') && key in diff.fromTable.nativeEnums) {
|
|
170
|
-
changedNativeEnums.push([column.nativeEnumName, column.enumItems, diff.fromTable.nativeEnums[key].items]);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
Utils.removeDuplicates(changedNativeEnums).forEach(([enumName, itemsNew, itemsOld]) => {
|
|
174
|
-
// postgres allows only adding new items
|
|
175
|
-
const newItems = itemsNew.filter(val => !itemsOld.includes(val));
|
|
176
|
-
if (enumName.includes('.')) {
|
|
177
|
-
const [enumSchemaName, rawEnumName] = enumName.split('.');
|
|
178
|
-
ret.push(...newItems.map(val => this.getAlterNativeEnumSQL(rawEnumName, enumSchemaName, val, itemsNew, itemsOld)));
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
ret.push(...newItems.map(val => this.getAlterNativeEnumSQL(enumName, schemaName, val, itemsNew, itemsOld)));
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
for (const index of Object.values(diff.removedIndexes)) {
|
|
185
|
-
ret.push(this.dropIndex(diff.name, index));
|
|
186
|
-
}
|
|
187
|
-
for (const index of Object.values(diff.changedIndexes)) {
|
|
188
|
-
ret.push(this.dropIndex(diff.name, index));
|
|
189
|
-
}
|
|
190
|
-
for (const check of Object.values(diff.removedChecks)) {
|
|
191
|
-
ret.push(this.dropConstraint(diff.name, check.name));
|
|
192
|
-
}
|
|
193
|
-
for (const check of Object.values(diff.changedChecks)) {
|
|
194
|
-
ret.push(this.dropConstraint(diff.name, check.name));
|
|
195
|
-
}
|
|
196
|
-
/* v8 ignore next */
|
|
197
|
-
if (!safe && Object.values(diff.removedColumns).length > 0) {
|
|
198
|
-
ret.push(this.getDropColumnsSQL(tableName, Object.values(diff.removedColumns), schemaName));
|
|
199
|
-
}
|
|
200
|
-
if (Object.values(diff.addedColumns).length > 0) {
|
|
201
|
-
this.append(ret, this.getAddColumnsSQL(diff.toTable, Object.values(diff.addedColumns)));
|
|
202
|
-
}
|
|
203
|
-
for (const column of Object.values(diff.addedColumns)) {
|
|
204
|
-
const foreignKey = Object.values(diff.addedForeignKeys).find(fk => fk.columnNames.length === 1 && fk.columnNames[0] === column.name);
|
|
205
|
-
if (foreignKey && this.options.createForeignKeyConstraints) {
|
|
206
|
-
delete diff.addedForeignKeys[foreignKey.constraintName];
|
|
207
|
-
ret.push(this.createForeignKey(diff.toTable, foreignKey));
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
for (const { column, changedProperties } of Object.values(diff.changedColumns)) {
|
|
211
|
-
if (changedProperties.size === 1 && changedProperties.has('comment')) {
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
if (changedProperties.size === 1 && changedProperties.has('enumItems') && column.nativeEnumName) {
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
this.append(ret, this.alterTableColumn(column, diff.fromTable, changedProperties));
|
|
218
|
-
}
|
|
219
|
-
for (const { column, changedProperties } of Object.values(diff.changedColumns).filter(diff => diff.changedProperties.has('comment'))) {
|
|
220
|
-
if (['type', 'nullable', 'autoincrement', 'unsigned', 'default', 'enumItems'].some(t => changedProperties.has(t))) {
|
|
221
|
-
continue; // will be handled via column update
|
|
222
|
-
}
|
|
223
|
-
ret.push(this.getChangeColumnCommentSQL(tableName, column, schemaName));
|
|
224
|
-
}
|
|
225
|
-
for (const [oldColumnName, column] of Object.entries(diff.renamedColumns)) {
|
|
226
|
-
ret.push(this.getRenameColumnSQL(tableName, oldColumnName, column, schemaName));
|
|
227
|
-
}
|
|
228
|
-
for (const foreignKey of Object.values(diff.addedForeignKeys)) {
|
|
229
|
-
ret.push(this.createForeignKey(diff.toTable, foreignKey));
|
|
230
|
-
}
|
|
231
|
-
for (const foreignKey of Object.values(diff.changedForeignKeys)) {
|
|
232
|
-
ret.push(this.createForeignKey(diff.toTable, foreignKey));
|
|
233
|
-
}
|
|
234
|
-
for (const index of Object.values(diff.addedIndexes)) {
|
|
235
|
-
ret.push(this.createIndex(index, diff.toTable));
|
|
236
|
-
}
|
|
237
|
-
for (const index of Object.values(diff.changedIndexes)) {
|
|
238
|
-
ret.push(this.createIndex(index, diff.toTable, true));
|
|
239
|
-
}
|
|
240
|
-
for (const [oldIndexName, index] of Object.entries(diff.renamedIndexes)) {
|
|
241
|
-
if (index.unique) {
|
|
242
|
-
ret.push(this.dropIndex(diff.name, index, oldIndexName));
|
|
243
|
-
ret.push(this.createIndex(index, diff.toTable));
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
ret.push(...this.getRenameIndexSQL(diff.name, index, oldIndexName));
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
for (const check of Object.values(diff.addedChecks)) {
|
|
250
|
-
ret.push(this.createCheck(diff.toTable, check));
|
|
251
|
-
}
|
|
252
|
-
for (const check of Object.values(diff.changedChecks)) {
|
|
253
|
-
ret.push(this.createCheck(diff.toTable, check));
|
|
254
|
-
}
|
|
255
|
-
if ('changedComment' in diff) {
|
|
256
|
-
ret.push(this.alterTableComment(diff.toTable, diff.changedComment));
|
|
257
|
-
}
|
|
258
|
-
return ret;
|
|
259
|
-
}
|
|
260
|
-
getAddColumnsSQL(table, columns) {
|
|
261
|
-
const adds = columns
|
|
262
|
-
.map(column => {
|
|
263
|
-
return `add ${this.createTableColumn(column, table)}`;
|
|
4
|
+
platform;
|
|
5
|
+
constructor(platform) {
|
|
6
|
+
this.platform = platform;
|
|
7
|
+
}
|
|
8
|
+
/** Returns SQL to prepend to schema migration scripts (e.g., disabling FK checks). */
|
|
9
|
+
getSchemaBeginning(_charset, disableForeignKeys) {
|
|
10
|
+
if (disableForeignKeys) {
|
|
11
|
+
return `${this.disableForeignKeysSQL()}\n`;
|
|
12
|
+
}
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
/** Returns SQL to disable foreign key checks. */
|
|
16
|
+
disableForeignKeysSQL() {
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
/** Returns SQL to re-enable foreign key checks. */
|
|
20
|
+
enableForeignKeysSQL() {
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
/** Returns SQL to append to schema migration scripts (e.g., re-enabling FK checks). */
|
|
24
|
+
getSchemaEnd(disableForeignKeys) {
|
|
25
|
+
if (disableForeignKeys) {
|
|
26
|
+
return `${this.enableForeignKeysSQL()}\n`;
|
|
27
|
+
}
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
finalizeTable(table, charset, collate) {
|
|
31
|
+
return '';
|
|
32
|
+
}
|
|
33
|
+
appendComments(table) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
supportsSchemaConstraints() {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
async getPrimaryKeys(connection, indexes = [], tableName, schemaName) {
|
|
40
|
+
const pks = indexes.filter(i => i.primary).map(pk => pk.columnNames);
|
|
41
|
+
return Utils.flatten(pks);
|
|
42
|
+
}
|
|
43
|
+
inferLengthFromColumnType(type) {
|
|
44
|
+
const match = /^\w+\s*(?:\(\s*(\d+)\s*\)|$)/.exec(type);
|
|
45
|
+
if (!match) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
return +match[1];
|
|
49
|
+
}
|
|
50
|
+
getTableKey(t) {
|
|
51
|
+
const unquote = str => str.replace(/['"`]/g, '');
|
|
52
|
+
const parts = t.table_name.split('.');
|
|
53
|
+
if (parts.length > 1) {
|
|
54
|
+
return `${unquote(parts[0])}.${unquote(parts[1])}`;
|
|
55
|
+
}
|
|
56
|
+
if (t.schema_name) {
|
|
57
|
+
return `${unquote(t.schema_name)}.${unquote(t.table_name)}`;
|
|
58
|
+
}
|
|
59
|
+
return unquote(t.table_name);
|
|
60
|
+
}
|
|
61
|
+
getCreateNativeEnumSQL(name, values, schema) {
|
|
62
|
+
throw new Error('Not supported by given driver');
|
|
63
|
+
}
|
|
64
|
+
getDropNativeEnumSQL(name, schema) {
|
|
65
|
+
throw new Error('Not supported by given driver');
|
|
66
|
+
}
|
|
67
|
+
getAlterNativeEnumSQL(name, schema, value, items, oldItems) {
|
|
68
|
+
throw new Error('Not supported by given driver');
|
|
69
|
+
}
|
|
70
|
+
/** Returns the SQL query to list all tables in the database. */
|
|
71
|
+
getListTablesSQL() {
|
|
72
|
+
throw new Error('Not supported by given driver');
|
|
73
|
+
}
|
|
74
|
+
/** Retrieves all tables from the database. */
|
|
75
|
+
async getAllTables(connection, schemas) {
|
|
76
|
+
return connection.execute(this.getListTablesSQL());
|
|
77
|
+
}
|
|
78
|
+
getListViewsSQL() {
|
|
79
|
+
throw new Error('Not supported by given driver');
|
|
80
|
+
}
|
|
81
|
+
async loadViews(schema, connection, schemaName) {
|
|
82
|
+
throw new Error('Not supported by given driver');
|
|
83
|
+
}
|
|
84
|
+
/** Returns SQL to rename a column in a table. */
|
|
85
|
+
getRenameColumnSQL(tableName, oldColumnName, to, schemaName) {
|
|
86
|
+
tableName = this.quote(tableName);
|
|
87
|
+
oldColumnName = this.quote(oldColumnName);
|
|
88
|
+
const columnName = this.quote(to.name);
|
|
89
|
+
const schemaReference = schemaName !== undefined && schemaName !== 'public' ? '"' + schemaName + '".' : '';
|
|
90
|
+
const tableReference = schemaReference + tableName;
|
|
91
|
+
return `alter table ${tableReference} rename column ${oldColumnName} to ${columnName}`;
|
|
92
|
+
}
|
|
93
|
+
/** Returns SQL to create an index on a table. */
|
|
94
|
+
getCreateIndexSQL(tableName, index) {
|
|
95
|
+
/* v8 ignore next */
|
|
96
|
+
if (index.expression) {
|
|
97
|
+
return index.expression;
|
|
98
|
+
}
|
|
99
|
+
if (index.fillFactor != null && (index.fillFactor < 0 || index.fillFactor > 100)) {
|
|
100
|
+
throw new Error(`fillFactor must be between 0 and 100, got ${index.fillFactor} for index '${index.keyName}'`);
|
|
101
|
+
}
|
|
102
|
+
tableName = this.quote(tableName);
|
|
103
|
+
const keyName = this.quote(index.keyName);
|
|
104
|
+
const defer = index.deferMode ? ` deferrable initially ${index.deferMode}` : '';
|
|
105
|
+
let sql = `create ${index.unique ? 'unique ' : ''}index ${keyName} on ${tableName}`;
|
|
106
|
+
if (index.unique && index.constraint) {
|
|
107
|
+
sql = `alter table ${tableName} add constraint ${keyName} unique`;
|
|
108
|
+
}
|
|
109
|
+
if (index.columnNames.some(column => column.includes('.'))) {
|
|
110
|
+
// JSON columns can have unique index but not unique constraint, and we need to distinguish those, so we can properly drop them
|
|
111
|
+
sql = `create ${index.unique ? 'unique ' : ''}index ${keyName} on ${tableName}`;
|
|
112
|
+
const columns = this.platform.getJsonIndexDefinition(index);
|
|
113
|
+
return `${sql} (${columns.join(', ')})${this.getCreateIndexSuffix(index)}${defer}`;
|
|
114
|
+
}
|
|
115
|
+
// Build column list with advanced options
|
|
116
|
+
const columns = this.getIndexColumns(index);
|
|
117
|
+
sql += ` (${columns})`;
|
|
118
|
+
// Add INCLUDE clause for covering indexes (PostgreSQL, MSSQL)
|
|
119
|
+
if (index.include?.length) {
|
|
120
|
+
sql += ` include (${index.include.map(c => this.quote(c)).join(', ')})`;
|
|
121
|
+
}
|
|
122
|
+
return sql + this.getCreateIndexSuffix(index) + defer;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Hook for adding driver-specific index options (e.g., fill factor for PostgreSQL).
|
|
126
|
+
*/
|
|
127
|
+
getCreateIndexSuffix(_index) {
|
|
128
|
+
return '';
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Build the column list for an index, supporting advanced options like sort order, nulls ordering, and collation.
|
|
132
|
+
* Note: Prefix length is only supported by MySQL/MariaDB which override this method.
|
|
133
|
+
*/
|
|
134
|
+
getIndexColumns(index) {
|
|
135
|
+
if (index.columns?.length) {
|
|
136
|
+
return index.columns
|
|
137
|
+
.map(col => {
|
|
138
|
+
let colDef = this.quote(col.name);
|
|
139
|
+
// Collation comes after column name (SQLite syntax: column COLLATE name)
|
|
140
|
+
if (col.collation) {
|
|
141
|
+
colDef += ` collate ${col.collation}`;
|
|
142
|
+
}
|
|
143
|
+
// Sort order
|
|
144
|
+
if (col.sort) {
|
|
145
|
+
colDef += ` ${col.sort}`;
|
|
146
|
+
}
|
|
147
|
+
// NULLS ordering (PostgreSQL)
|
|
148
|
+
if (col.nulls) {
|
|
149
|
+
colDef += ` nulls ${col.nulls}`;
|
|
150
|
+
}
|
|
151
|
+
return colDef;
|
|
264
152
|
})
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
153
|
+
.join(', ');
|
|
154
|
+
}
|
|
155
|
+
return index.columnNames.map(c => this.quote(c)).join(', ');
|
|
156
|
+
}
|
|
157
|
+
/** Returns SQL to drop an index. */
|
|
158
|
+
getDropIndexSQL(tableName, index) {
|
|
159
|
+
return `drop index ${this.quote(index.keyName)}`;
|
|
160
|
+
}
|
|
161
|
+
getRenameIndexSQL(tableName, index, oldIndexName) {
|
|
162
|
+
return [
|
|
163
|
+
this.getDropIndexSQL(tableName, { ...index, keyName: oldIndexName }),
|
|
164
|
+
this.getCreateIndexSQL(tableName, index),
|
|
165
|
+
];
|
|
166
|
+
}
|
|
167
|
+
/** Returns SQL statements to apply a table difference (add/drop/alter columns, indexes, foreign keys). */
|
|
168
|
+
alterTable(diff, safe) {
|
|
169
|
+
const ret = [];
|
|
170
|
+
const [schemaName, tableName] = this.splitTableName(diff.name);
|
|
171
|
+
if (this.platform.supportsNativeEnums()) {
|
|
172
|
+
const changedNativeEnums = [];
|
|
173
|
+
for (const { column, changedProperties } of Object.values(diff.changedColumns)) {
|
|
174
|
+
if (!column.nativeEnumName) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const key =
|
|
178
|
+
schemaName && schemaName !== this.platform.getDefaultSchemaName() && !column.nativeEnumName.includes('.')
|
|
179
|
+
? schemaName + '.' + column.nativeEnumName
|
|
180
|
+
: column.nativeEnumName;
|
|
181
|
+
if (changedProperties.has('enumItems') && key in diff.fromTable.nativeEnums) {
|
|
182
|
+
changedNativeEnums.push([column.nativeEnumName, column.enumItems, diff.fromTable.nativeEnums[key].items]);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
Utils.removeDuplicates(changedNativeEnums).forEach(([enumName, itemsNew, itemsOld]) => {
|
|
186
|
+
// postgres allows only adding new items
|
|
187
|
+
const newItems = itemsNew.filter(val => !itemsOld.includes(val));
|
|
188
|
+
if (enumName.includes('.')) {
|
|
189
|
+
const [enumSchemaName, rawEnumName] = enumName.split('.');
|
|
190
|
+
ret.push(
|
|
191
|
+
...newItems.map(val => this.getAlterNativeEnumSQL(rawEnumName, enumSchemaName, val, itemsNew, itemsOld)),
|
|
192
|
+
);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
ret.push(...newItems.map(val => this.getAlterNativeEnumSQL(enumName, schemaName, val, itemsNew, itemsOld)));
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
for (const index of Object.values(diff.removedIndexes)) {
|
|
199
|
+
ret.push(this.dropIndex(diff.name, index));
|
|
200
|
+
}
|
|
201
|
+
for (const index of Object.values(diff.changedIndexes)) {
|
|
202
|
+
ret.push(this.dropIndex(diff.name, index));
|
|
203
|
+
}
|
|
204
|
+
for (const check of Object.values(diff.removedChecks)) {
|
|
205
|
+
ret.push(this.dropConstraint(diff.name, check.name));
|
|
206
|
+
}
|
|
207
|
+
for (const check of Object.values(diff.changedChecks)) {
|
|
208
|
+
ret.push(this.dropConstraint(diff.name, check.name));
|
|
280
209
|
}
|
|
281
210
|
/* v8 ignore next */
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
return
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
211
|
+
if (!safe && Object.values(diff.removedColumns).length > 0) {
|
|
212
|
+
ret.push(this.getDropColumnsSQL(tableName, Object.values(diff.removedColumns), schemaName));
|
|
213
|
+
}
|
|
214
|
+
if (Object.values(diff.addedColumns).length > 0) {
|
|
215
|
+
this.append(ret, this.getAddColumnsSQL(diff.toTable, Object.values(diff.addedColumns)));
|
|
216
|
+
}
|
|
217
|
+
for (const column of Object.values(diff.addedColumns)) {
|
|
218
|
+
const foreignKey = Object.values(diff.addedForeignKeys).find(
|
|
219
|
+
fk => fk.columnNames.length === 1 && fk.columnNames[0] === column.name,
|
|
220
|
+
);
|
|
221
|
+
if (foreignKey && this.options.createForeignKeyConstraints) {
|
|
222
|
+
delete diff.addedForeignKeys[foreignKey.constraintName];
|
|
223
|
+
ret.push(this.createForeignKey(diff.toTable, foreignKey));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
for (const { column, changedProperties } of Object.values(diff.changedColumns)) {
|
|
227
|
+
if (changedProperties.size === 1 && changedProperties.has('comment')) {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (changedProperties.size === 1 && changedProperties.has('enumItems') && column.nativeEnumName) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
this.append(ret, this.alterTableColumn(column, diff.fromTable, changedProperties));
|
|
234
|
+
}
|
|
235
|
+
for (const { column, changedProperties } of Object.values(diff.changedColumns).filter(diff =>
|
|
236
|
+
diff.changedProperties.has('comment'),
|
|
237
|
+
)) {
|
|
238
|
+
if (
|
|
239
|
+
['type', 'nullable', 'autoincrement', 'unsigned', 'default', 'enumItems'].some(t => changedProperties.has(t))
|
|
240
|
+
) {
|
|
241
|
+
continue; // will be handled via column update
|
|
242
|
+
}
|
|
243
|
+
ret.push(this.getChangeColumnCommentSQL(tableName, column, schemaName));
|
|
244
|
+
}
|
|
245
|
+
for (const [oldColumnName, column] of Object.entries(diff.renamedColumns)) {
|
|
246
|
+
ret.push(this.getRenameColumnSQL(tableName, oldColumnName, column, schemaName));
|
|
247
|
+
}
|
|
248
|
+
for (const foreignKey of Object.values(diff.addedForeignKeys)) {
|
|
249
|
+
ret.push(this.createForeignKey(diff.toTable, foreignKey));
|
|
250
|
+
}
|
|
251
|
+
for (const foreignKey of Object.values(diff.changedForeignKeys)) {
|
|
252
|
+
ret.push(this.createForeignKey(diff.toTable, foreignKey));
|
|
253
|
+
}
|
|
254
|
+
for (const index of Object.values(diff.addedIndexes)) {
|
|
255
|
+
ret.push(this.createIndex(index, diff.toTable));
|
|
256
|
+
}
|
|
257
|
+
for (const index of Object.values(diff.changedIndexes)) {
|
|
258
|
+
ret.push(this.createIndex(index, diff.toTable, true));
|
|
259
|
+
}
|
|
260
|
+
for (const [oldIndexName, index] of Object.entries(diff.renamedIndexes)) {
|
|
261
|
+
if (index.unique) {
|
|
262
|
+
ret.push(this.dropIndex(diff.name, index, oldIndexName));
|
|
263
|
+
ret.push(this.createIndex(index, diff.toTable));
|
|
264
|
+
} else {
|
|
265
|
+
ret.push(...this.getRenameIndexSQL(diff.name, index, oldIndexName));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
for (const check of Object.values(diff.addedChecks)) {
|
|
269
|
+
ret.push(this.createCheck(diff.toTable, check));
|
|
270
|
+
}
|
|
271
|
+
for (const check of Object.values(diff.changedChecks)) {
|
|
272
|
+
ret.push(this.createCheck(diff.toTable, check));
|
|
273
|
+
}
|
|
274
|
+
if ('changedComment' in diff) {
|
|
275
|
+
ret.push(this.alterTableComment(diff.toTable, diff.changedComment));
|
|
276
|
+
}
|
|
277
|
+
return ret;
|
|
278
|
+
}
|
|
279
|
+
/** Returns SQL to add columns to an existing table. */
|
|
280
|
+
getAddColumnsSQL(table, columns) {
|
|
281
|
+
const adds = columns
|
|
282
|
+
.map(column => {
|
|
283
|
+
return `add ${this.createTableColumn(column, table)}`;
|
|
284
|
+
})
|
|
285
|
+
.join(', ');
|
|
286
|
+
return [`alter table ${table.getQuotedName()} ${adds}`];
|
|
287
|
+
}
|
|
288
|
+
getDropColumnsSQL(tableName, columns, schemaName) {
|
|
289
|
+
const name = this.quote(this.getTableName(tableName, schemaName));
|
|
290
|
+
const drops = columns.map(column => `drop column ${this.quote(column.name)}`).join(', ');
|
|
291
|
+
return `alter table ${name} ${drops}`;
|
|
292
|
+
}
|
|
293
|
+
hasNonDefaultPrimaryKeyName(table) {
|
|
294
|
+
const pkIndex = table.getPrimaryKey();
|
|
295
|
+
if (!pkIndex || !this.platform.supportsCustomPrimaryKeyNames()) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
const defaultName = this.platform.getDefaultPrimaryName(table.name, pkIndex.columnNames);
|
|
299
|
+
return pkIndex?.keyName !== defaultName;
|
|
300
|
+
}
|
|
301
|
+
/* v8 ignore next */
|
|
302
|
+
castColumn(name, type) {
|
|
303
|
+
return '';
|
|
304
|
+
}
|
|
305
|
+
alterTableColumn(column, table, changedProperties) {
|
|
306
|
+
const sql = [];
|
|
307
|
+
if (changedProperties.has('default') && column.default == null) {
|
|
308
|
+
sql.push(`alter table ${table.getQuotedName()} alter column ${this.quote(column.name)} drop default`);
|
|
309
|
+
}
|
|
310
|
+
if (changedProperties.has('type')) {
|
|
311
|
+
let type = column.type + (column.generated ? ` generated always as ${column.generated}` : '');
|
|
312
|
+
if (column.nativeEnumName) {
|
|
313
|
+
type = this.quote(this.getTableName(type, table.schema));
|
|
314
|
+
}
|
|
315
|
+
sql.push(
|
|
316
|
+
`alter table ${table.getQuotedName()} alter column ${this.quote(column.name)} type ${type + this.castColumn(column.name, type)}`,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
if (changedProperties.has('default') && column.default != null) {
|
|
320
|
+
sql.push(
|
|
321
|
+
`alter table ${table.getQuotedName()} alter column ${this.quote(column.name)} set default ${column.default}`,
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
if (changedProperties.has('nullable')) {
|
|
325
|
+
const action = column.nullable ? 'drop' : 'set';
|
|
326
|
+
sql.push(`alter table ${table.getQuotedName()} alter column ${this.quote(column.name)} ${action} not null`);
|
|
327
|
+
}
|
|
328
|
+
return sql;
|
|
329
|
+
}
|
|
330
|
+
createTableColumn(column, table, changedProperties) {
|
|
331
|
+
const compositePK = table.getPrimaryKey()?.composite;
|
|
332
|
+
const primaryKey = !changedProperties && !this.hasNonDefaultPrimaryKeyName(table);
|
|
333
|
+
const columnType = column.type + (column.generated ? ` generated always as ${column.generated}` : '');
|
|
334
|
+
const useDefault = column.default != null && column.default !== 'null' && !column.autoincrement;
|
|
335
|
+
const col = [this.quote(column.name), columnType];
|
|
336
|
+
Utils.runIfNotEmpty(() => col.push('unsigned'), column.unsigned && this.platform.supportsUnsigned());
|
|
337
|
+
Utils.runIfNotEmpty(() => col.push('null'), column.nullable);
|
|
338
|
+
Utils.runIfNotEmpty(() => col.push('not null'), !column.nullable && !column.generated);
|
|
339
|
+
Utils.runIfNotEmpty(() => col.push('auto_increment'), column.autoincrement);
|
|
340
|
+
Utils.runIfNotEmpty(() => col.push('unique'), column.autoincrement && !column.primary);
|
|
341
|
+
if (
|
|
342
|
+
column.autoincrement &&
|
|
343
|
+
!column.generated &&
|
|
344
|
+
!compositePK &&
|
|
345
|
+
(!changedProperties || changedProperties.has('autoincrement') || changedProperties.has('type'))
|
|
346
|
+
) {
|
|
347
|
+
Utils.runIfNotEmpty(() => col.push('primary key'), primaryKey && column.primary);
|
|
348
|
+
}
|
|
349
|
+
if (useDefault) {
|
|
350
|
+
// https://dev.mysql.com/doc/refman/9.0/en/data-type-defaults.html
|
|
351
|
+
const needsExpression = [
|
|
352
|
+
'blob',
|
|
353
|
+
'text',
|
|
354
|
+
'json',
|
|
355
|
+
'point',
|
|
356
|
+
'linestring',
|
|
357
|
+
'polygon',
|
|
358
|
+
'multipoint',
|
|
359
|
+
'multilinestring',
|
|
360
|
+
'multipolygon',
|
|
361
|
+
'geometrycollection',
|
|
362
|
+
].some(type => column.type.toLowerCase().startsWith(type));
|
|
363
|
+
const defaultSql = needsExpression && !column.default.startsWith('(') ? `(${column.default})` : column.default;
|
|
364
|
+
col.push(`default ${defaultSql}`);
|
|
365
|
+
}
|
|
366
|
+
Utils.runIfNotEmpty(() => col.push(column.extra), column.extra);
|
|
367
|
+
Utils.runIfNotEmpty(() => col.push(`comment ${this.platform.quoteValue(column.comment)}`), column.comment);
|
|
368
|
+
return col.join(' ');
|
|
369
|
+
}
|
|
370
|
+
getPreAlterTable(tableDiff, safe) {
|
|
371
|
+
return [];
|
|
372
|
+
}
|
|
373
|
+
getPostAlterTable(tableDiff, safe) {
|
|
374
|
+
return [];
|
|
375
|
+
}
|
|
376
|
+
getChangeColumnCommentSQL(tableName, to, schemaName) {
|
|
377
|
+
return '';
|
|
378
|
+
}
|
|
379
|
+
async getNamespaces(connection) {
|
|
380
|
+
return [];
|
|
381
|
+
}
|
|
382
|
+
async mapIndexes(indexes) {
|
|
383
|
+
const map = {};
|
|
384
|
+
indexes.forEach(index => {
|
|
385
|
+
if (map[index.keyName]) {
|
|
386
|
+
if (index.columnNames.length > 0) {
|
|
387
|
+
map[index.keyName].composite = true;
|
|
388
|
+
map[index.keyName].columnNames.push(index.columnNames[0]);
|
|
389
|
+
}
|
|
390
|
+
// Merge columns array for advanced column options (sort, length, collation, etc.)
|
|
391
|
+
if (index.columns?.length) {
|
|
392
|
+
map[index.keyName].columns ??= [];
|
|
393
|
+
map[index.keyName].columns.push(index.columns[0]);
|
|
415
394
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
395
|
+
// Merge INCLUDE columns
|
|
396
|
+
if (index.include?.length) {
|
|
397
|
+
map[index.keyName].include ??= [];
|
|
398
|
+
map[index.keyName].include.push(index.include[0]);
|
|
399
|
+
}
|
|
400
|
+
} else {
|
|
401
|
+
map[index.keyName] = index;
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
return Object.values(map);
|
|
405
|
+
}
|
|
406
|
+
mapForeignKeys(fks, tableName, schemaName) {
|
|
407
|
+
return fks.reduce((ret, fk) => {
|
|
408
|
+
if (ret[fk.constraint_name]) {
|
|
409
|
+
ret[fk.constraint_name].columnNames.push(fk.column_name);
|
|
410
|
+
ret[fk.constraint_name].referencedColumnNames.push(fk.referenced_column_name);
|
|
411
|
+
} else {
|
|
412
|
+
ret[fk.constraint_name] = {
|
|
413
|
+
columnNames: [fk.column_name],
|
|
414
|
+
constraintName: fk.constraint_name,
|
|
415
|
+
localTableName: schemaName ? `${schemaName}.${tableName}` : tableName,
|
|
416
|
+
referencedTableName: fk.referenced_schema_name
|
|
417
|
+
? `${fk.referenced_schema_name}.${fk.referenced_table_name}`
|
|
418
|
+
: fk.referenced_table_name,
|
|
419
|
+
referencedColumnNames: [fk.referenced_column_name],
|
|
420
|
+
updateRule: fk.update_rule.toLowerCase(),
|
|
421
|
+
deleteRule: fk.delete_rule.toLowerCase(),
|
|
422
|
+
deferMode: fk.defer_mode,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
return ret;
|
|
426
|
+
}, {});
|
|
427
|
+
}
|
|
428
|
+
normalizeDefaultValue(defaultValue, length, defaultValues = {}) {
|
|
429
|
+
if (defaultValue == null) {
|
|
430
|
+
return defaultValue;
|
|
431
|
+
}
|
|
432
|
+
if (defaultValue instanceof RawQueryFragment) {
|
|
433
|
+
return this.platform.formatQuery(defaultValue.sql, defaultValue.params);
|
|
434
|
+
}
|
|
435
|
+
const genericValue = defaultValue.replace(/\(\d+\)/, '(?)').toLowerCase();
|
|
436
|
+
const norm = defaultValues[genericValue];
|
|
437
|
+
if (!norm) {
|
|
438
|
+
return defaultValue;
|
|
439
|
+
}
|
|
440
|
+
return norm[0].replace('(?)', length != null ? `(${length})` : '');
|
|
441
|
+
}
|
|
442
|
+
getCreateDatabaseSQL(name) {
|
|
443
|
+
name = this.quote(name);
|
|
444
|
+
// two line breaks to force separate execution
|
|
445
|
+
return `create database ${name};\n\nuse ${name}`;
|
|
446
|
+
}
|
|
447
|
+
getDropDatabaseSQL(name) {
|
|
448
|
+
return `drop database if exists ${this.quote(name)}`;
|
|
449
|
+
}
|
|
450
|
+
/* v8 ignore next */
|
|
451
|
+
getCreateNamespaceSQL(name) {
|
|
452
|
+
return `create schema if not exists ${this.quote(name)}`;
|
|
453
|
+
}
|
|
454
|
+
/* v8 ignore next */
|
|
455
|
+
getDropNamespaceSQL(name) {
|
|
456
|
+
return `drop schema if exists ${this.quote(name)}`;
|
|
457
|
+
}
|
|
458
|
+
getDatabaseExistsSQL(name) {
|
|
459
|
+
return `select 1 from information_schema.schemata where schema_name = '${name}'`;
|
|
460
|
+
}
|
|
461
|
+
getDatabaseNotExistsError(dbName) {
|
|
462
|
+
return `Unknown database '${dbName}'`;
|
|
463
|
+
}
|
|
464
|
+
getManagementDbName() {
|
|
465
|
+
return 'information_schema';
|
|
466
|
+
}
|
|
467
|
+
getDefaultEmptyString() {
|
|
468
|
+
return "''";
|
|
469
|
+
}
|
|
470
|
+
async databaseExists(connection, name) {
|
|
471
|
+
try {
|
|
472
|
+
const res = await connection.execute(this.getDatabaseExistsSQL(name));
|
|
473
|
+
return res.length > 0;
|
|
474
|
+
} catch (e) {
|
|
475
|
+
if (e instanceof Error && e.message.includes(this.getDatabaseNotExistsError(name))) {
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
/* v8 ignore next */
|
|
479
|
+
throw e;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
append(array, sql, pad = false) {
|
|
483
|
+
const length = array.length;
|
|
484
|
+
for (const row of Utils.asArray(sql)) {
|
|
485
|
+
if (!row) {
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
let tmp = row.trim();
|
|
489
|
+
if (!tmp.endsWith(';')) {
|
|
490
|
+
tmp += ';';
|
|
491
|
+
}
|
|
492
|
+
array.push(tmp);
|
|
493
|
+
}
|
|
494
|
+
if (pad && array.length > length) {
|
|
495
|
+
array.push('');
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
/** Returns SQL statements to create a table with all its columns, primary key, indexes, and checks. */
|
|
499
|
+
createTable(table, alter) {
|
|
500
|
+
let sql = `create table ${table.getQuotedName()} (`;
|
|
501
|
+
const columns = table.getColumns();
|
|
502
|
+
const lastColumn = columns[columns.length - 1].name;
|
|
503
|
+
for (const column of columns) {
|
|
504
|
+
const col = this.createTableColumn(column, table);
|
|
505
|
+
if (col) {
|
|
506
|
+
const comma = column.name === lastColumn ? '' : ', ';
|
|
507
|
+
sql += col + comma;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
const primaryKey = table.getPrimaryKey();
|
|
511
|
+
const createPrimary =
|
|
512
|
+
!table.getColumns().some(c => c.autoincrement && c.primary) || this.hasNonDefaultPrimaryKeyName(table);
|
|
513
|
+
if (createPrimary && primaryKey) {
|
|
514
|
+
const name = this.hasNonDefaultPrimaryKeyName(table) ? `constraint ${this.quote(primaryKey.keyName)} ` : '';
|
|
515
|
+
sql += `, ${name}primary key (${primaryKey.columnNames.map(c => this.quote(c)).join(', ')})`;
|
|
516
|
+
}
|
|
517
|
+
sql += ')';
|
|
518
|
+
sql += this.finalizeTable(
|
|
519
|
+
table,
|
|
520
|
+
this.platform.getConfig().get('charset'),
|
|
521
|
+
this.platform.getConfig().get('collate'),
|
|
522
|
+
);
|
|
523
|
+
const ret = [];
|
|
524
|
+
this.append(ret, sql);
|
|
525
|
+
this.append(ret, this.appendComments(table));
|
|
526
|
+
for (const index of table.getIndexes()) {
|
|
527
|
+
this.append(ret, this.createIndex(index, table));
|
|
528
|
+
}
|
|
529
|
+
if (!alter) {
|
|
530
|
+
for (const check of table.getChecks()) {
|
|
531
|
+
this.append(ret, this.createCheck(table, check));
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return ret;
|
|
535
|
+
}
|
|
536
|
+
alterTableComment(table, comment) {
|
|
537
|
+
return `alter table ${table.getQuotedName()} comment = ${this.platform.quoteValue(comment ?? '')}`;
|
|
538
|
+
}
|
|
539
|
+
/** Returns SQL to create a foreign key constraint on a table. */
|
|
540
|
+
createForeignKey(table, foreignKey, alterTable = true, inline = false) {
|
|
541
|
+
if (!this.options.createForeignKeyConstraints) {
|
|
542
|
+
return '';
|
|
543
|
+
}
|
|
544
|
+
const constraintName = this.quote(foreignKey.constraintName);
|
|
545
|
+
const columnNames = foreignKey.columnNames.map(c => this.quote(c)).join(', ');
|
|
546
|
+
const referencedColumnNames = foreignKey.referencedColumnNames.map(c => this.quote(c)).join(', ');
|
|
547
|
+
const referencedTableName = this.quote(this.getReferencedTableName(foreignKey.referencedTableName, table.schema));
|
|
548
|
+
const sql = [];
|
|
549
|
+
if (alterTable) {
|
|
550
|
+
sql.push(`alter table ${table.getQuotedName()} add`);
|
|
551
|
+
}
|
|
552
|
+
sql.push(`constraint ${constraintName}`);
|
|
553
|
+
if (!inline) {
|
|
554
|
+
sql.push(`foreign key (${columnNames})`);
|
|
555
|
+
}
|
|
556
|
+
sql.push(`references ${referencedTableName} (${referencedColumnNames})`);
|
|
557
|
+
if (foreignKey.localTableName !== foreignKey.referencedTableName || this.platform.supportsMultipleCascadePaths()) {
|
|
558
|
+
if (foreignKey.updateRule) {
|
|
559
|
+
sql.push(`on update ${foreignKey.updateRule}`);
|
|
560
|
+
}
|
|
561
|
+
if (foreignKey.deleteRule) {
|
|
562
|
+
sql.push(`on delete ${foreignKey.deleteRule}`);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (foreignKey.deferMode) {
|
|
566
|
+
sql.push(`deferrable initially ${foreignKey.deferMode}`);
|
|
567
|
+
}
|
|
568
|
+
return sql.join(' ');
|
|
569
|
+
}
|
|
570
|
+
splitTableName(name, skipDefaultSchema = false) {
|
|
571
|
+
const parts = name.split('.');
|
|
572
|
+
const tableName = parts.pop();
|
|
573
|
+
let schemaName = parts.pop();
|
|
574
|
+
if (skipDefaultSchema && schemaName === this.platform.getDefaultSchemaName()) {
|
|
575
|
+
schemaName = undefined;
|
|
576
|
+
}
|
|
577
|
+
return [schemaName, tableName];
|
|
578
|
+
}
|
|
579
|
+
getReferencedTableName(referencedTableName, schema) {
|
|
580
|
+
const [schemaName, tableName] = this.splitTableName(referencedTableName);
|
|
581
|
+
schema = schemaName ?? schema ?? this.platform.getConfig().get('schema');
|
|
430
582
|
/* v8 ignore next */
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
sql.push(`on delete ${foreignKey.deleteRule}`);
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
if (foreignKey.deferMode) {
|
|
536
|
-
sql.push(`deferrable initially ${foreignKey.deferMode}`);
|
|
537
|
-
}
|
|
538
|
-
return sql.join(' ');
|
|
539
|
-
}
|
|
540
|
-
splitTableName(name, skipDefaultSchema = false) {
|
|
541
|
-
const parts = name.split('.');
|
|
542
|
-
const tableName = parts.pop();
|
|
543
|
-
let schemaName = parts.pop();
|
|
544
|
-
if (skipDefaultSchema && schemaName === this.platform.getDefaultSchemaName()) {
|
|
545
|
-
schemaName = undefined;
|
|
546
|
-
}
|
|
547
|
-
return [schemaName, tableName];
|
|
548
|
-
}
|
|
549
|
-
getReferencedTableName(referencedTableName, schema) {
|
|
550
|
-
const [schemaName, tableName] = this.splitTableName(referencedTableName);
|
|
551
|
-
schema = schemaName ?? schema ?? this.platform.getConfig().get('schema');
|
|
552
|
-
/* v8 ignore next */
|
|
553
|
-
if (schema && schemaName === '*') {
|
|
554
|
-
return `${schema}.${referencedTableName.replace(/^\*\./, '')}`;
|
|
555
|
-
}
|
|
556
|
-
return this.getTableName(tableName, schema);
|
|
557
|
-
}
|
|
558
|
-
createIndex(index, table, createPrimary = false) {
|
|
559
|
-
if (index.primary && !createPrimary) {
|
|
560
|
-
return '';
|
|
561
|
-
}
|
|
562
|
-
if (index.expression) {
|
|
563
|
-
return index.expression;
|
|
564
|
-
}
|
|
565
|
-
const columns = index.columnNames.map(c => this.quote(c)).join(', ');
|
|
566
|
-
const defer = index.deferMode ? ` deferrable initially ${index.deferMode}` : '';
|
|
567
|
-
if (index.primary) {
|
|
568
|
-
const keyName = this.hasNonDefaultPrimaryKeyName(table) ? `constraint ${index.keyName} ` : '';
|
|
569
|
-
return `alter table ${table.getQuotedName()} add ${keyName}primary key (${columns})${defer}`;
|
|
570
|
-
}
|
|
571
|
-
if (index.type === 'fulltext') {
|
|
572
|
-
const columns = index.columnNames.map(name => ({ name, type: table.getColumn(name).type }));
|
|
573
|
-
if (this.platform.supportsCreatingFullTextIndex()) {
|
|
574
|
-
return this.platform.getFullTextIndexExpression(index.keyName, table.schema, table.name, columns);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
return this.getCreateIndexSQL(table.getShortestName(), index);
|
|
578
|
-
}
|
|
579
|
-
createCheck(table, check) {
|
|
580
|
-
return `alter table ${table.getQuotedName()} add constraint ${this.quote(check.name)} check (${check.expression})`;
|
|
581
|
-
}
|
|
582
|
-
getTableName(table, schema) {
|
|
583
|
-
if (schema && schema !== this.platform.getDefaultSchemaName()) {
|
|
584
|
-
return `${schema}.${table}`;
|
|
585
|
-
}
|
|
586
|
-
return table;
|
|
587
|
-
}
|
|
588
|
-
getTablesGroupedBySchemas(tables) {
|
|
589
|
-
return tables.reduce((acc, table) => {
|
|
590
|
-
const schemaTables = acc.get(table.schema_name);
|
|
591
|
-
if (!schemaTables) {
|
|
592
|
-
acc.set(table.schema_name, [table]);
|
|
593
|
-
return acc;
|
|
594
|
-
}
|
|
595
|
-
schemaTables.push(table);
|
|
596
|
-
return acc;
|
|
597
|
-
}, new Map());
|
|
598
|
-
}
|
|
599
|
-
get options() {
|
|
600
|
-
return this.platform.getConfig().get('schemaGenerator');
|
|
601
|
-
}
|
|
602
|
-
processComment(comment) {
|
|
603
|
-
return comment;
|
|
604
|
-
}
|
|
605
|
-
quote(...keys) {
|
|
606
|
-
return this.platform.quoteIdentifier(keys.filter(Boolean).join('.'));
|
|
607
|
-
}
|
|
608
|
-
dropForeignKey(tableName, constraintName) {
|
|
609
|
-
return `alter table ${this.quote(tableName)} drop foreign key ${this.quote(constraintName)}`;
|
|
610
|
-
}
|
|
611
|
-
dropIndex(table, index, oldIndexName = index.keyName) {
|
|
612
|
-
if (index.primary) {
|
|
613
|
-
return `alter table ${this.quote(table)} drop primary key`;
|
|
614
|
-
}
|
|
615
|
-
return `alter table ${this.quote(table)} drop index ${this.quote(oldIndexName)}`;
|
|
616
|
-
}
|
|
617
|
-
dropConstraint(table, name) {
|
|
618
|
-
return `alter table ${this.quote(table)} drop constraint ${this.quote(name)}`;
|
|
619
|
-
}
|
|
620
|
-
dropTableIfExists(name, schema) {
|
|
621
|
-
let sql = `drop table if exists ${this.quote(this.getTableName(name, schema))}`;
|
|
622
|
-
if (this.platform.usesCascadeStatement()) {
|
|
623
|
-
sql += ' cascade';
|
|
624
|
-
}
|
|
625
|
-
return sql;
|
|
626
|
-
}
|
|
627
|
-
createView(name, schema, definition) {
|
|
628
|
-
const viewName = this.quote(this.getTableName(name, schema));
|
|
629
|
-
return `create view ${viewName} as ${definition}`;
|
|
630
|
-
}
|
|
631
|
-
dropViewIfExists(name, schema) {
|
|
632
|
-
let sql = `drop view if exists ${this.quote(this.getTableName(name, schema))}`;
|
|
633
|
-
if (this.platform.usesCascadeStatement()) {
|
|
634
|
-
sql += ' cascade';
|
|
635
|
-
}
|
|
636
|
-
return sql;
|
|
637
|
-
}
|
|
638
|
-
createMaterializedView(name, schema, definition, withData = true) {
|
|
639
|
-
throw new Error('Not supported by given driver');
|
|
640
|
-
}
|
|
641
|
-
dropMaterializedViewIfExists(name, schema) {
|
|
642
|
-
throw new Error('Not supported by given driver');
|
|
643
|
-
}
|
|
644
|
-
refreshMaterializedView(name, schema, concurrently = false) {
|
|
645
|
-
throw new Error('Not supported by given driver');
|
|
646
|
-
}
|
|
647
|
-
getListMaterializedViewsSQL() {
|
|
648
|
-
throw new Error('Not supported by given driver');
|
|
649
|
-
}
|
|
650
|
-
async loadMaterializedViews(schema, connection, schemaName) {
|
|
651
|
-
throw new Error('Not supported by given driver');
|
|
652
|
-
}
|
|
583
|
+
if (schema && schemaName === '*') {
|
|
584
|
+
return `${schema}.${referencedTableName.replace(/^\*\./, '')}`;
|
|
585
|
+
}
|
|
586
|
+
return this.getTableName(tableName, schema);
|
|
587
|
+
}
|
|
588
|
+
createIndex(index, table, createPrimary = false) {
|
|
589
|
+
if (index.primary && !createPrimary) {
|
|
590
|
+
return '';
|
|
591
|
+
}
|
|
592
|
+
if (index.expression) {
|
|
593
|
+
return index.expression;
|
|
594
|
+
}
|
|
595
|
+
const columns = index.columnNames.map(c => this.quote(c)).join(', ');
|
|
596
|
+
const defer = index.deferMode ? ` deferrable initially ${index.deferMode}` : '';
|
|
597
|
+
if (index.primary) {
|
|
598
|
+
const keyName = this.hasNonDefaultPrimaryKeyName(table) ? `constraint ${index.keyName} ` : '';
|
|
599
|
+
return `alter table ${table.getQuotedName()} add ${keyName}primary key (${columns})${defer}`;
|
|
600
|
+
}
|
|
601
|
+
if (index.type === 'fulltext') {
|
|
602
|
+
const columns = index.columnNames.map(name => ({ name, type: table.getColumn(name).type }));
|
|
603
|
+
if (this.platform.supportsCreatingFullTextIndex()) {
|
|
604
|
+
return this.platform.getFullTextIndexExpression(index.keyName, table.schema, table.name, columns);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return this.getCreateIndexSQL(table.getShortestName(), index);
|
|
608
|
+
}
|
|
609
|
+
createCheck(table, check) {
|
|
610
|
+
return `alter table ${table.getQuotedName()} add constraint ${this.quote(check.name)} check (${check.expression})`;
|
|
611
|
+
}
|
|
612
|
+
getTableName(table, schema) {
|
|
613
|
+
if (schema && schema !== this.platform.getDefaultSchemaName()) {
|
|
614
|
+
return `${schema}.${table}`;
|
|
615
|
+
}
|
|
616
|
+
return table;
|
|
617
|
+
}
|
|
618
|
+
getTablesGroupedBySchemas(tables) {
|
|
619
|
+
return tables.reduce((acc, table) => {
|
|
620
|
+
const schemaTables = acc.get(table.schema_name);
|
|
621
|
+
if (!schemaTables) {
|
|
622
|
+
acc.set(table.schema_name, [table]);
|
|
623
|
+
return acc;
|
|
624
|
+
}
|
|
625
|
+
schemaTables.push(table);
|
|
626
|
+
return acc;
|
|
627
|
+
}, new Map());
|
|
628
|
+
}
|
|
629
|
+
get options() {
|
|
630
|
+
return this.platform.getConfig().get('schemaGenerator');
|
|
631
|
+
}
|
|
632
|
+
processComment(comment) {
|
|
633
|
+
return comment;
|
|
634
|
+
}
|
|
635
|
+
quote(...keys) {
|
|
636
|
+
return this.platform.quoteIdentifier(keys.filter(Boolean).join('.'));
|
|
637
|
+
}
|
|
638
|
+
dropForeignKey(tableName, constraintName) {
|
|
639
|
+
return `alter table ${this.quote(tableName)} drop foreign key ${this.quote(constraintName)}`;
|
|
640
|
+
}
|
|
641
|
+
dropIndex(table, index, oldIndexName = index.keyName) {
|
|
642
|
+
if (index.primary) {
|
|
643
|
+
return `alter table ${this.quote(table)} drop primary key`;
|
|
644
|
+
}
|
|
645
|
+
return `alter table ${this.quote(table)} drop index ${this.quote(oldIndexName)}`;
|
|
646
|
+
}
|
|
647
|
+
dropConstraint(table, name) {
|
|
648
|
+
return `alter table ${this.quote(table)} drop constraint ${this.quote(name)}`;
|
|
649
|
+
}
|
|
650
|
+
/** Returns SQL to drop a table if it exists. */
|
|
651
|
+
dropTableIfExists(name, schema) {
|
|
652
|
+
let sql = `drop table if exists ${this.quote(this.getTableName(name, schema))}`;
|
|
653
|
+
if (this.platform.usesCascadeStatement()) {
|
|
654
|
+
sql += ' cascade';
|
|
655
|
+
}
|
|
656
|
+
return sql;
|
|
657
|
+
}
|
|
658
|
+
createView(name, schema, definition) {
|
|
659
|
+
const viewName = this.quote(this.getTableName(name, schema));
|
|
660
|
+
return `create view ${viewName} as ${definition}`;
|
|
661
|
+
}
|
|
662
|
+
dropViewIfExists(name, schema) {
|
|
663
|
+
let sql = `drop view if exists ${this.quote(this.getTableName(name, schema))}`;
|
|
664
|
+
if (this.platform.usesCascadeStatement()) {
|
|
665
|
+
sql += ' cascade';
|
|
666
|
+
}
|
|
667
|
+
return sql;
|
|
668
|
+
}
|
|
669
|
+
createMaterializedView(name, schema, definition, withData = true) {
|
|
670
|
+
throw new Error('Not supported by given driver');
|
|
671
|
+
}
|
|
672
|
+
dropMaterializedViewIfExists(name, schema) {
|
|
673
|
+
throw new Error('Not supported by given driver');
|
|
674
|
+
}
|
|
675
|
+
refreshMaterializedView(name, schema, concurrently = false) {
|
|
676
|
+
throw new Error('Not supported by given driver');
|
|
677
|
+
}
|
|
678
|
+
getListMaterializedViewsSQL() {
|
|
679
|
+
throw new Error('Not supported by given driver');
|
|
680
|
+
}
|
|
681
|
+
async loadMaterializedViews(schema, connection, schemaName) {
|
|
682
|
+
throw new Error('Not supported by given driver');
|
|
683
|
+
}
|
|
653
684
|
}
|