@mikro-orm/sql 7.0.2 → 7.0.3-dev.0

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