@mikro-orm/sql 7.0.3-dev.8 → 7.0.3

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 (87) hide show
  1. package/AbstractSqlConnection.d.ts +94 -58
  2. package/AbstractSqlConnection.js +235 -238
  3. package/AbstractSqlDriver.d.ts +410 -155
  4. package/AbstractSqlDriver.js +2064 -1937
  5. package/AbstractSqlPlatform.d.ts +83 -73
  6. package/AbstractSqlPlatform.js +162 -158
  7. package/PivotCollectionPersister.d.ts +33 -15
  8. package/PivotCollectionPersister.js +158 -160
  9. package/README.md +2 -2
  10. package/SqlEntityManager.d.ts +67 -22
  11. package/SqlEntityManager.js +54 -38
  12. package/SqlEntityRepository.d.ts +14 -14
  13. package/SqlEntityRepository.js +23 -23
  14. package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +12 -12
  15. package/dialects/mssql/MsSqlNativeQueryBuilder.js +192 -194
  16. package/dialects/mysql/BaseMySqlPlatform.d.ts +64 -45
  17. package/dialects/mysql/BaseMySqlPlatform.js +134 -131
  18. package/dialects/mysql/MySqlExceptionConverter.d.ts +6 -6
  19. package/dialects/mysql/MySqlExceptionConverter.js +91 -77
  20. package/dialects/mysql/MySqlNativeQueryBuilder.d.ts +3 -3
  21. package/dialects/mysql/MySqlNativeQueryBuilder.js +66 -69
  22. package/dialects/mysql/MySqlSchemaHelper.d.ts +39 -39
  23. package/dialects/mysql/MySqlSchemaHelper.js +327 -319
  24. package/dialects/oracledb/OracleDialect.d.ts +81 -52
  25. package/dialects/oracledb/OracleDialect.js +155 -149
  26. package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +12 -12
  27. package/dialects/oracledb/OracleNativeQueryBuilder.js +232 -236
  28. package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +108 -105
  29. package/dialects/postgresql/BasePostgreSqlPlatform.js +351 -350
  30. package/dialects/postgresql/FullTextType.d.ts +10 -6
  31. package/dialects/postgresql/FullTextType.js +51 -51
  32. package/dialects/postgresql/PostgreSqlExceptionConverter.d.ts +5 -5
  33. package/dialects/postgresql/PostgreSqlExceptionConverter.js +55 -43
  34. package/dialects/postgresql/PostgreSqlNativeQueryBuilder.d.ts +1 -1
  35. package/dialects/postgresql/PostgreSqlNativeQueryBuilder.js +4 -4
  36. package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +102 -82
  37. package/dialects/postgresql/PostgreSqlSchemaHelper.js +733 -683
  38. package/dialects/sqlite/BaseSqliteConnection.d.ts +3 -5
  39. package/dialects/sqlite/BaseSqliteConnection.js +21 -19
  40. package/dialects/sqlite/NodeSqliteDialect.d.ts +1 -1
  41. package/dialects/sqlite/NodeSqliteDialect.js +23 -23
  42. package/dialects/sqlite/SqliteDriver.d.ts +1 -1
  43. package/dialects/sqlite/SqliteDriver.js +3 -3
  44. package/dialects/sqlite/SqliteExceptionConverter.d.ts +6 -6
  45. package/dialects/sqlite/SqliteExceptionConverter.js +67 -51
  46. package/dialects/sqlite/SqliteNativeQueryBuilder.d.ts +2 -2
  47. package/dialects/sqlite/SqliteNativeQueryBuilder.js +7 -7
  48. package/dialects/sqlite/SqlitePlatform.d.ts +63 -72
  49. package/dialects/sqlite/SqlitePlatform.js +139 -139
  50. package/dialects/sqlite/SqliteSchemaHelper.d.ts +70 -60
  51. package/dialects/sqlite/SqliteSchemaHelper.js +533 -520
  52. package/package.json +3 -3
  53. package/plugin/index.d.ts +42 -35
  54. package/plugin/index.js +43 -36
  55. package/plugin/transformer.d.ts +117 -94
  56. package/plugin/transformer.js +890 -881
  57. package/query/ArrayCriteriaNode.d.ts +4 -4
  58. package/query/ArrayCriteriaNode.js +18 -18
  59. package/query/CriteriaNode.d.ts +35 -25
  60. package/query/CriteriaNode.js +133 -123
  61. package/query/CriteriaNodeFactory.d.ts +49 -6
  62. package/query/CriteriaNodeFactory.js +97 -94
  63. package/query/NativeQueryBuilder.d.ts +118 -118
  64. package/query/NativeQueryBuilder.js +484 -480
  65. package/query/ObjectCriteriaNode.d.ts +12 -12
  66. package/query/ObjectCriteriaNode.js +298 -282
  67. package/query/QueryBuilder.d.ts +1546 -904
  68. package/query/QueryBuilder.js +2294 -2144
  69. package/query/QueryBuilderHelper.d.ts +153 -72
  70. package/query/QueryBuilderHelper.js +1079 -1028
  71. package/query/ScalarCriteriaNode.d.ts +3 -3
  72. package/query/ScalarCriteriaNode.js +53 -46
  73. package/query/enums.d.ts +14 -14
  74. package/query/enums.js +14 -14
  75. package/query/raw.d.ts +16 -6
  76. package/query/raw.js +10 -10
  77. package/schema/DatabaseSchema.d.ts +73 -50
  78. package/schema/DatabaseSchema.js +331 -307
  79. package/schema/DatabaseTable.d.ts +96 -73
  80. package/schema/DatabaseTable.js +1012 -927
  81. package/schema/SchemaComparator.d.ts +70 -66
  82. package/schema/SchemaComparator.js +766 -740
  83. package/schema/SchemaHelper.d.ts +109 -95
  84. package/schema/SchemaHelper.js +675 -659
  85. package/schema/SqlSchemaGenerator.d.ts +78 -58
  86. package/schema/SqlSchemaGenerator.js +535 -501
  87. package/typings.d.ts +380 -266
@@ -1,668 +1,684 @@
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;
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)}`;
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;
276
152
  })
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;
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));
292
209
  }
293
210
  /* v8 ignore next */
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`);
301
- }
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;
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]);
427
394
  }
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
- }
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');
442
582
  /* v8 ignore next */
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
- }
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
+ }
668
684
  }