@mikro-orm/sql 7.0.2-dev.9 → 7.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AbstractSqlConnection.d.ts +95 -47
- package/AbstractSqlConnection.js +240 -232
- package/AbstractSqlDriver.d.ts +412 -155
- package/AbstractSqlDriver.js +2062 -1937
- package/AbstractSqlPlatform.d.ts +84 -73
- package/AbstractSqlPlatform.js +163 -158
- package/PivotCollectionPersister.d.ts +33 -15
- package/PivotCollectionPersister.js +158 -160
- package/README.md +128 -294
- package/SqlEntityManager.d.ts +68 -20
- package/SqlEntityManager.js +54 -37
- package/SqlEntityRepository.d.ts +15 -14
- package/SqlEntityRepository.js +24 -23
- package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +12 -12
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +192 -194
- package/dialects/mysql/BaseMySqlPlatform.d.ts +64 -45
- package/dialects/mysql/BaseMySqlPlatform.js +134 -131
- package/dialects/mysql/MySqlExceptionConverter.d.ts +6 -6
- package/dialects/mysql/MySqlExceptionConverter.js +91 -77
- package/dialects/mysql/MySqlNativeQueryBuilder.d.ts +3 -3
- package/dialects/mysql/MySqlNativeQueryBuilder.js +66 -69
- package/dialects/mysql/MySqlSchemaHelper.d.ts +39 -39
- package/dialects/mysql/MySqlSchemaHelper.js +327 -319
- package/dialects/oracledb/OracleDialect.d.ts +81 -52
- package/dialects/oracledb/OracleDialect.js +155 -149
- package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +12 -12
- package/dialects/oracledb/OracleNativeQueryBuilder.js +232 -236
- package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +108 -105
- package/dialects/postgresql/BasePostgreSqlPlatform.js +351 -350
- package/dialects/postgresql/FullTextType.d.ts +10 -6
- package/dialects/postgresql/FullTextType.js +51 -51
- package/dialects/postgresql/PostgreSqlExceptionConverter.d.ts +5 -5
- package/dialects/postgresql/PostgreSqlExceptionConverter.js +55 -43
- package/dialects/postgresql/PostgreSqlNativeQueryBuilder.d.ts +1 -1
- package/dialects/postgresql/PostgreSqlNativeQueryBuilder.js +4 -4
- package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +102 -82
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +711 -683
- package/dialects/sqlite/BaseSqliteConnection.d.ts +3 -5
- package/dialects/sqlite/BaseSqliteConnection.js +21 -19
- package/dialects/sqlite/NodeSqliteDialect.d.ts +1 -1
- package/dialects/sqlite/NodeSqliteDialect.js +23 -23
- package/dialects/sqlite/SqliteDriver.d.ts +1 -1
- package/dialects/sqlite/SqliteDriver.js +3 -3
- package/dialects/sqlite/SqliteExceptionConverter.d.ts +6 -6
- package/dialects/sqlite/SqliteExceptionConverter.js +67 -51
- package/dialects/sqlite/SqliteNativeQueryBuilder.d.ts +2 -2
- package/dialects/sqlite/SqliteNativeQueryBuilder.js +7 -7
- package/dialects/sqlite/SqlitePlatform.d.ts +63 -72
- package/dialects/sqlite/SqlitePlatform.js +139 -139
- package/dialects/sqlite/SqliteSchemaHelper.d.ts +70 -60
- package/dialects/sqlite/SqliteSchemaHelper.js +533 -520
- package/package.json +4 -4
- package/plugin/index.d.ts +44 -35
- package/plugin/index.js +44 -36
- package/plugin/transformer.d.ts +117 -94
- package/plugin/transformer.js +890 -881
- package/query/ArrayCriteriaNode.d.ts +4 -4
- package/query/ArrayCriteriaNode.js +18 -18
- package/query/CriteriaNode.d.ts +35 -25
- package/query/CriteriaNode.js +133 -123
- package/query/CriteriaNodeFactory.d.ts +49 -6
- package/query/CriteriaNodeFactory.js +97 -94
- package/query/NativeQueryBuilder.d.ts +120 -117
- package/query/NativeQueryBuilder.js +484 -480
- package/query/ObjectCriteriaNode.d.ts +12 -12
- package/query/ObjectCriteriaNode.js +298 -282
- package/query/QueryBuilder.d.ts +1546 -904
- package/query/QueryBuilder.js +2270 -2145
- package/query/QueryBuilderHelper.d.ts +153 -72
- package/query/QueryBuilderHelper.js +1079 -1028
- package/query/ScalarCriteriaNode.d.ts +3 -3
- package/query/ScalarCriteriaNode.js +53 -46
- package/query/enums.d.ts +16 -14
- package/query/enums.js +16 -14
- package/query/raw.d.ts +16 -6
- package/query/raw.js +10 -10
- package/schema/DatabaseSchema.d.ts +73 -50
- package/schema/DatabaseSchema.js +331 -307
- package/schema/DatabaseTable.d.ts +96 -73
- package/schema/DatabaseTable.js +1012 -927
- package/schema/SchemaComparator.d.ts +58 -54
- package/schema/SchemaComparator.js +745 -719
- package/schema/SchemaHelper.d.ts +110 -80
- package/schema/SchemaHelper.js +676 -645
- package/schema/SqlSchemaGenerator.d.ts +79 -58
- package/schema/SqlSchemaGenerator.js +536 -501
- package/typings.d.ts +380 -266
|
@@ -1,198 +1,197 @@
|
|
|
1
1
|
import { EnumType, StringType, TextType } from '@mikro-orm/core';
|
|
2
2
|
import { SchemaHelper } from '../../schema/SchemaHelper.js';
|
|
3
3
|
export class MySqlSchemaHelper extends SchemaHelper {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
return `set names ${charset};\n\n`;
|
|
4
|
+
#cache = {};
|
|
5
|
+
static DEFAULT_VALUES = {
|
|
6
|
+
'now()': ['now()', 'current_timestamp'],
|
|
7
|
+
'current_timestamp(?)': ['current_timestamp(?)'],
|
|
8
|
+
0: ['0', 'false'],
|
|
9
|
+
};
|
|
10
|
+
getSchemaBeginning(charset, disableForeignKeys) {
|
|
11
|
+
if (disableForeignKeys) {
|
|
12
|
+
return `set names ${charset};\n${this.disableForeignKeysSQL()}\n\n`;
|
|
15
13
|
}
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
return `set names ${charset};\n\n`;
|
|
15
|
+
}
|
|
16
|
+
disableForeignKeysSQL() {
|
|
17
|
+
return 'set foreign_key_checks = 0;';
|
|
18
|
+
}
|
|
19
|
+
enableForeignKeysSQL() {
|
|
20
|
+
return 'set foreign_key_checks = 1;';
|
|
21
|
+
}
|
|
22
|
+
finalizeTable(table, charset, collate) {
|
|
23
|
+
let sql = ` default character set ${charset}`;
|
|
24
|
+
if (collate) {
|
|
25
|
+
sql += ` collate ${collate}`;
|
|
18
26
|
}
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
sql += ' engine = InnoDB';
|
|
28
|
+
if (table.comment) {
|
|
29
|
+
sql += ` comment = ${this.platform.quoteValue(table.comment)}`;
|
|
21
30
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
return sql;
|
|
32
|
+
}
|
|
33
|
+
getListTablesSQL() {
|
|
34
|
+
return `select table_name as table_name, nullif(table_schema, schema()) as schema_name, table_comment as table_comment from information_schema.tables where table_type = 'BASE TABLE' and table_schema = schema()`;
|
|
35
|
+
}
|
|
36
|
+
getListViewsSQL() {
|
|
37
|
+
return `select table_name as view_name, nullif(table_schema, schema()) as schema_name, view_definition from information_schema.views where table_schema = schema()`;
|
|
38
|
+
}
|
|
39
|
+
async loadViews(schema, connection, schemaName) {
|
|
40
|
+
const views = await connection.execute(this.getListViewsSQL());
|
|
41
|
+
for (const view of views) {
|
|
42
|
+
// MySQL information_schema.views.view_definition requires SHOW VIEW privilege
|
|
43
|
+
// and may return NULL. Use SHOW CREATE VIEW as fallback.
|
|
44
|
+
let definition = view.view_definition?.trim();
|
|
45
|
+
if (!definition) {
|
|
46
|
+
const createView = await connection.execute(`show create view \`${view.view_name}\``);
|
|
47
|
+
if (createView[0]?.['Create View']) {
|
|
48
|
+
// Extract SELECT statement from CREATE VIEW ... AS SELECT ...
|
|
49
|
+
const match = /\bAS\s+(.+)$/is.exec(createView[0]['Create View']);
|
|
50
|
+
definition = match?.[1]?.trim();
|
|
30
51
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
52
|
+
}
|
|
53
|
+
if (definition) {
|
|
54
|
+
schema.addView(view.view_name, view.schema_name ?? undefined, definition);
|
|
55
|
+
}
|
|
35
56
|
}
|
|
36
|
-
|
|
37
|
-
|
|
57
|
+
}
|
|
58
|
+
async loadInformationSchema(schema, connection, tables) {
|
|
59
|
+
if (tables.length === 0) {
|
|
60
|
+
return;
|
|
38
61
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const match = /\bAS\s+(.+)$/is.exec(createView[0]['Create View']);
|
|
50
|
-
definition = match?.[1]?.trim();
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
if (definition) {
|
|
54
|
-
schema.addView(view.view_name, view.schema_name ?? undefined, definition);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
62
|
+
const columns = await this.getAllColumns(connection, tables);
|
|
63
|
+
const indexes = await this.getAllIndexes(connection, tables);
|
|
64
|
+
const checks = await this.getAllChecks(connection, tables);
|
|
65
|
+
const fks = await this.getAllForeignKeys(connection, tables);
|
|
66
|
+
const enums = await this.getAllEnumDefinitions(connection, tables);
|
|
67
|
+
for (const t of tables) {
|
|
68
|
+
const key = this.getTableKey(t);
|
|
69
|
+
const table = schema.addTable(t.table_name, t.schema_name, t.table_comment);
|
|
70
|
+
const pks = await this.getPrimaryKeys(connection, indexes[key], table.name, table.schema);
|
|
71
|
+
table.init(columns[key], indexes[key], checks[key], pks, fks[key], enums[key]);
|
|
57
72
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
const columns = await this.getAllColumns(connection, tables);
|
|
63
|
-
const indexes = await this.getAllIndexes(connection, tables);
|
|
64
|
-
const checks = await this.getAllChecks(connection, tables);
|
|
65
|
-
const fks = await this.getAllForeignKeys(connection, tables);
|
|
66
|
-
const enums = await this.getAllEnumDefinitions(connection, tables);
|
|
67
|
-
for (const t of tables) {
|
|
68
|
-
const key = this.getTableKey(t);
|
|
69
|
-
const table = schema.addTable(t.table_name, t.schema_name, t.table_comment);
|
|
70
|
-
const pks = await this.getPrimaryKeys(connection, indexes[key], table.name, table.schema);
|
|
71
|
-
table.init(columns[key], indexes[key], checks[key], pks, fks[key], enums[key]);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
async getAllIndexes(connection, tables) {
|
|
75
|
-
const sql = `select table_name as table_name, nullif(table_schema, schema()) as schema_name, index_name as index_name, non_unique as non_unique, column_name as column_name, index_type as index_type, sub_part as sub_part, collation as sort_order /*!80013 , expression as expression, is_visible as is_visible */
|
|
73
|
+
}
|
|
74
|
+
async getAllIndexes(connection, tables) {
|
|
75
|
+
const sql = `select table_name as table_name, nullif(table_schema, schema()) as schema_name, index_name as index_name, non_unique as non_unique, column_name as column_name, index_type as index_type, sub_part as sub_part, collation as sort_order /*!80013 , expression as expression, is_visible as is_visible */
|
|
76
76
|
from information_schema.statistics where table_schema = database()
|
|
77
77
|
and table_name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(', ')})
|
|
78
78
|
order by schema_name, table_name, index_name, seq_in_index`;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
else if (index.index_type === 'SPATIAL') {
|
|
105
|
-
/* v8 ignore next */
|
|
106
|
-
indexDef.type = 'spatial';
|
|
107
|
-
}
|
|
108
|
-
// Capture invisible flag (MySQL 8.0.13+)
|
|
109
|
-
if (index.is_visible === 'NO') {
|
|
110
|
-
indexDef.invisible = true;
|
|
111
|
-
}
|
|
112
|
-
if (!index.column_name || index.expression?.match(/ where /i)) {
|
|
113
|
-
indexDef.expression = index.expression; // required for the `getCreateIndexSQL()` call
|
|
114
|
-
indexDef.expression = this.getCreateIndexSQL(index.table_name, indexDef, !!index.expression);
|
|
115
|
-
}
|
|
116
|
-
ret[key] ??= [];
|
|
117
|
-
ret[key].push(indexDef);
|
|
118
|
-
}
|
|
119
|
-
for (const key of Object.keys(ret)) {
|
|
120
|
-
ret[key] = await this.mapIndexes(ret[key]);
|
|
121
|
-
}
|
|
122
|
-
return ret;
|
|
123
|
-
}
|
|
124
|
-
getCreateIndexSQL(tableName, index, partialExpression = false) {
|
|
79
|
+
const allIndexes = await connection.execute(sql);
|
|
80
|
+
const ret = {};
|
|
81
|
+
for (const index of allIndexes) {
|
|
82
|
+
const key = this.getTableKey(index);
|
|
83
|
+
const indexDef = {
|
|
84
|
+
columnNames: [index.column_name],
|
|
85
|
+
keyName: index.index_name,
|
|
86
|
+
unique: !index.non_unique,
|
|
87
|
+
primary: index.index_name === 'PRIMARY',
|
|
88
|
+
constraint: !index.non_unique,
|
|
89
|
+
};
|
|
90
|
+
// Capture column options (prefix length, sort order)
|
|
91
|
+
if (index.sub_part != null || index.sort_order === 'D') {
|
|
92
|
+
indexDef.columns = [
|
|
93
|
+
{
|
|
94
|
+
name: index.column_name,
|
|
95
|
+
...(index.sub_part != null && { length: index.sub_part }),
|
|
96
|
+
...(index.sort_order === 'D' && { sort: 'DESC' }),
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
}
|
|
100
|
+
// Capture index type for fulltext and spatial indexes
|
|
101
|
+
if (index.index_type === 'FULLTEXT') {
|
|
102
|
+
indexDef.type = 'fulltext';
|
|
103
|
+
} else if (index.index_type === 'SPATIAL') {
|
|
125
104
|
/* v8 ignore next */
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const columns = this.platform.getJsonIndexDefinition(index);
|
|
139
|
-
sql = `alter table ${tableName} add ${index.unique ? 'unique ' : ''}index ${keyName} `;
|
|
140
|
-
sql += `(${columns.join(', ')})`;
|
|
141
|
-
return this.appendMySqlIndexSuffix(sql, index);
|
|
142
|
-
}
|
|
143
|
-
// Build column list with advanced options
|
|
144
|
-
const columns = this.getIndexColumns(index);
|
|
145
|
-
sql += `(${columns})`;
|
|
146
|
-
return this.appendMySqlIndexSuffix(sql, index);
|
|
105
|
+
indexDef.type = 'spatial';
|
|
106
|
+
}
|
|
107
|
+
// Capture invisible flag (MySQL 8.0.13+)
|
|
108
|
+
if (index.is_visible === 'NO') {
|
|
109
|
+
indexDef.invisible = true;
|
|
110
|
+
}
|
|
111
|
+
if (!index.column_name || index.expression?.match(/ where /i)) {
|
|
112
|
+
indexDef.expression = index.expression; // required for the `getCreateIndexSQL()` call
|
|
113
|
+
indexDef.expression = this.getCreateIndexSQL(index.table_name, indexDef, !!index.expression);
|
|
114
|
+
}
|
|
115
|
+
ret[key] ??= [];
|
|
116
|
+
ret[key].push(indexDef);
|
|
147
117
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
* MySQL requires collation to be specified as an expression: (column_name COLLATE collation_name)
|
|
151
|
-
*/
|
|
152
|
-
getIndexColumns(index) {
|
|
153
|
-
if (index.columns?.length) {
|
|
154
|
-
return index.columns
|
|
155
|
-
.map(col => {
|
|
156
|
-
const quotedName = this.quote(col.name);
|
|
157
|
-
// MySQL supports collation via expression: (column_name COLLATE collation_name)
|
|
158
|
-
// When collation is specified, wrap in parentheses as an expression
|
|
159
|
-
if (col.collation) {
|
|
160
|
-
let expr = col.length ? `${quotedName}(${col.length})` : quotedName;
|
|
161
|
-
expr = `(${expr} collate ${col.collation})`;
|
|
162
|
-
// Sort order comes after the expression
|
|
163
|
-
if (col.sort) {
|
|
164
|
-
expr += ` ${col.sort}`;
|
|
165
|
-
}
|
|
166
|
-
return expr;
|
|
167
|
-
}
|
|
168
|
-
// Standard column definition without collation
|
|
169
|
-
let colDef = quotedName;
|
|
170
|
-
// MySQL supports prefix length
|
|
171
|
-
if (col.length) {
|
|
172
|
-
colDef += `(${col.length})`;
|
|
173
|
-
}
|
|
174
|
-
// MySQL supports sort order
|
|
175
|
-
if (col.sort) {
|
|
176
|
-
colDef += ` ${col.sort}`;
|
|
177
|
-
}
|
|
178
|
-
return colDef;
|
|
179
|
-
})
|
|
180
|
-
.join(', ');
|
|
181
|
-
}
|
|
182
|
-
return index.columnNames.map(c => this.quote(c)).join(', ');
|
|
118
|
+
for (const key of Object.keys(ret)) {
|
|
119
|
+
ret[key] = await this.mapIndexes(ret[key]);
|
|
183
120
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
sql += ' invisible';
|
|
191
|
-
}
|
|
192
|
-
return sql;
|
|
121
|
+
return ret;
|
|
122
|
+
}
|
|
123
|
+
getCreateIndexSQL(tableName, index, partialExpression = false) {
|
|
124
|
+
/* v8 ignore next */
|
|
125
|
+
if (index.expression && !partialExpression) {
|
|
126
|
+
return index.expression;
|
|
193
127
|
}
|
|
194
|
-
|
|
195
|
-
|
|
128
|
+
tableName = this.quote(tableName);
|
|
129
|
+
const keyName = this.quote(index.keyName);
|
|
130
|
+
let sql = `alter table ${tableName} add ${index.unique ? 'unique' : 'index'} ${keyName} `;
|
|
131
|
+
if (index.expression && partialExpression) {
|
|
132
|
+
sql += `(${index.expression})`;
|
|
133
|
+
return this.appendMySqlIndexSuffix(sql, index);
|
|
134
|
+
}
|
|
135
|
+
// JSON columns can have unique index but not unique constraint, and we need to distinguish those, so we can properly drop them
|
|
136
|
+
if (index.columnNames.some(column => column.includes('.'))) {
|
|
137
|
+
const columns = this.platform.getJsonIndexDefinition(index);
|
|
138
|
+
sql = `alter table ${tableName} add ${index.unique ? 'unique ' : ''}index ${keyName} `;
|
|
139
|
+
sql += `(${columns.join(', ')})`;
|
|
140
|
+
return this.appendMySqlIndexSuffix(sql, index);
|
|
141
|
+
}
|
|
142
|
+
// Build column list with advanced options
|
|
143
|
+
const columns = this.getIndexColumns(index);
|
|
144
|
+
sql += `(${columns})`;
|
|
145
|
+
return this.appendMySqlIndexSuffix(sql, index);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Build the column list for a MySQL index, with MySQL-specific handling for collation.
|
|
149
|
+
* MySQL requires collation to be specified as an expression: (column_name COLLATE collation_name)
|
|
150
|
+
*/
|
|
151
|
+
getIndexColumns(index) {
|
|
152
|
+
if (index.columns?.length) {
|
|
153
|
+
return index.columns
|
|
154
|
+
.map(col => {
|
|
155
|
+
const quotedName = this.quote(col.name);
|
|
156
|
+
// MySQL supports collation via expression: (column_name COLLATE collation_name)
|
|
157
|
+
// When collation is specified, wrap in parentheses as an expression
|
|
158
|
+
if (col.collation) {
|
|
159
|
+
let expr = col.length ? `${quotedName}(${col.length})` : quotedName;
|
|
160
|
+
expr = `(${expr} collate ${col.collation})`;
|
|
161
|
+
// Sort order comes after the expression
|
|
162
|
+
if (col.sort) {
|
|
163
|
+
expr += ` ${col.sort}`;
|
|
164
|
+
}
|
|
165
|
+
return expr;
|
|
166
|
+
}
|
|
167
|
+
// Standard column definition without collation
|
|
168
|
+
let colDef = quotedName;
|
|
169
|
+
// MySQL supports prefix length
|
|
170
|
+
if (col.length) {
|
|
171
|
+
colDef += `(${col.length})`;
|
|
172
|
+
}
|
|
173
|
+
// MySQL supports sort order
|
|
174
|
+
if (col.sort) {
|
|
175
|
+
colDef += ` ${col.sort}`;
|
|
176
|
+
}
|
|
177
|
+
return colDef;
|
|
178
|
+
})
|
|
179
|
+
.join(', ');
|
|
180
|
+
}
|
|
181
|
+
return index.columnNames.map(c => this.quote(c)).join(', ');
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Append MySQL-specific index suffixes like INVISIBLE.
|
|
185
|
+
*/
|
|
186
|
+
appendMySqlIndexSuffix(sql, index) {
|
|
187
|
+
// MySQL 8.0+ supports INVISIBLE indexes
|
|
188
|
+
if (index.invisible) {
|
|
189
|
+
sql += ' invisible';
|
|
190
|
+
}
|
|
191
|
+
return sql;
|
|
192
|
+
}
|
|
193
|
+
async getAllColumns(connection, tables) {
|
|
194
|
+
const sql = `select table_name as table_name,
|
|
196
195
|
nullif(table_schema, schema()) as schema_name,
|
|
197
196
|
column_name as column_name,
|
|
198
197
|
column_default as column_default,
|
|
@@ -208,151 +207,160 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
208
207
|
ifnull(datetime_precision, character_maximum_length) length
|
|
209
208
|
from information_schema.columns where table_schema = database() and table_name in (${tables.map(t => this.platform.quoteValue(t.table_name))})
|
|
210
209
|
order by ordinal_position`;
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
210
|
+
const allColumns = await connection.execute(sql);
|
|
211
|
+
const str = val => (val != null ? '' + val : val);
|
|
212
|
+
const extra = val =>
|
|
213
|
+
val.replace(/auto_increment|default_generated|(stored|virtual) generated/i, '').trim() || undefined;
|
|
214
|
+
const ret = {};
|
|
215
|
+
for (const col of allColumns) {
|
|
216
|
+
const mappedType = this.platform.getMappedType(col.column_type);
|
|
217
|
+
const defaultValue = str(
|
|
218
|
+
this.normalizeDefaultValue(
|
|
219
|
+
mappedType.compareAsType() === 'boolean' && ['0', '1'].includes(col.column_default)
|
|
220
|
+
? ['false', 'true'][+col.column_default]
|
|
221
|
+
: col.column_default,
|
|
222
|
+
col.length,
|
|
223
|
+
),
|
|
224
|
+
);
|
|
225
|
+
const key = this.getTableKey(col);
|
|
226
|
+
const generated = col.generation_expression
|
|
227
|
+
? `(${col.generation_expression.replaceAll(`\\'`, `'`)}) ${col.extra.match(/stored generated/i) ? 'stored' : 'virtual'}`
|
|
228
|
+
: undefined;
|
|
229
|
+
ret[key] ??= [];
|
|
230
|
+
ret[key].push({
|
|
231
|
+
name: col.column_name,
|
|
232
|
+
type: this.platform.isNumericColumn(mappedType)
|
|
233
|
+
? col.column_type.replace(/ unsigned$/, '').replace(/\(\d+\)$/, '')
|
|
234
|
+
: col.column_type,
|
|
235
|
+
mappedType,
|
|
236
|
+
unsigned: col.column_type.endsWith(' unsigned'),
|
|
237
|
+
length: col.length,
|
|
238
|
+
default: this.wrap(defaultValue, mappedType),
|
|
239
|
+
nullable: col.is_nullable === 'YES',
|
|
240
|
+
primary: col.column_key === 'PRI',
|
|
241
|
+
unique: col.column_key === 'UNI',
|
|
242
|
+
autoincrement: col.extra === 'auto_increment',
|
|
243
|
+
precision: col.numeric_precision,
|
|
244
|
+
scale: col.numeric_scale,
|
|
245
|
+
comment: col.column_comment,
|
|
246
|
+
extra: extra(col.extra),
|
|
247
|
+
generated,
|
|
248
|
+
});
|
|
246
249
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const allChecks = await connection.execute(sql);
|
|
254
|
-
const ret = {};
|
|
255
|
-
for (const check of allChecks) {
|
|
256
|
-
const key = this.getTableKey(check);
|
|
257
|
-
ret[key] ??= [];
|
|
258
|
-
ret[key].push({
|
|
259
|
-
name: check.name,
|
|
260
|
-
columnName: check.column_name,
|
|
261
|
-
definition: `check ${check.expression}`,
|
|
262
|
-
expression: check.expression.replace(/^\((.*)\)$/, '$1'),
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
return ret;
|
|
250
|
+
return ret;
|
|
251
|
+
}
|
|
252
|
+
async getAllChecks(connection, tables) {
|
|
253
|
+
/* v8 ignore next */
|
|
254
|
+
if (!(await this.supportsCheckConstraints(connection))) {
|
|
255
|
+
return {};
|
|
266
256
|
}
|
|
267
|
-
|
|
268
|
-
|
|
257
|
+
const sql = this.getChecksSQL(tables);
|
|
258
|
+
const allChecks = await connection.execute(sql);
|
|
259
|
+
const ret = {};
|
|
260
|
+
for (const check of allChecks) {
|
|
261
|
+
const key = this.getTableKey(check);
|
|
262
|
+
ret[key] ??= [];
|
|
263
|
+
ret[key].push({
|
|
264
|
+
name: check.name,
|
|
265
|
+
columnName: check.column_name,
|
|
266
|
+
definition: `check ${check.expression}`,
|
|
267
|
+
expression: check.expression.replace(/^\((.*)\)$/, '$1'),
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
return ret;
|
|
271
|
+
}
|
|
272
|
+
async getAllForeignKeys(connection, tables) {
|
|
273
|
+
const sql = `select k.constraint_name as constraint_name, nullif(k.table_schema, schema()) as schema_name, k.table_name as table_name, k.column_name as column_name, k.referenced_table_name as referenced_table_name, k.referenced_column_name as referenced_column_name, c.update_rule as update_rule, c.delete_rule as delete_rule
|
|
269
274
|
from information_schema.key_column_usage k
|
|
270
275
|
inner join information_schema.referential_constraints c on c.constraint_name = k.constraint_name and c.table_name = k.table_name
|
|
271
276
|
where k.table_name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(', ')})
|
|
272
277
|
and k.table_schema = database() and c.constraint_schema = database() and k.referenced_column_name is not null
|
|
273
278
|
order by constraint_name, k.ordinal_position`;
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
Object.keys(ret).forEach(key => {
|
|
282
|
-
const parts = key.split('.');
|
|
283
|
-
/* v8 ignore next */
|
|
284
|
-
const schemaName = parts.length > 1 ? parts[0] : undefined;
|
|
285
|
-
ret[key] = this.mapForeignKeys(ret[key], key, schemaName);
|
|
286
|
-
});
|
|
287
|
-
return ret;
|
|
279
|
+
const allFks = await connection.execute(sql);
|
|
280
|
+
const ret = {};
|
|
281
|
+
for (const fk of allFks) {
|
|
282
|
+
const key = this.getTableKey(fk);
|
|
283
|
+
ret[key] ??= [];
|
|
284
|
+
ret[key].push(fk);
|
|
288
285
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
tableName = this.quote(tableName);
|
|
303
|
-
oldColumnName = this.quote(oldColumnName);
|
|
304
|
-
const columnName = this.quote(to.name);
|
|
305
|
-
return `alter table ${tableName} change ${oldColumnName} ${columnName} ${this.getColumnDeclarationSQL(to)}`;
|
|
306
|
-
}
|
|
307
|
-
getRenameIndexSQL(tableName, index, oldIndexName) {
|
|
308
|
-
tableName = this.quote(tableName);
|
|
309
|
-
oldIndexName = this.quote(oldIndexName);
|
|
310
|
-
const keyName = this.quote(index.keyName);
|
|
311
|
-
return [`alter table ${tableName} rename index ${oldIndexName} to ${keyName}`];
|
|
312
|
-
}
|
|
313
|
-
getChangeColumnCommentSQL(tableName, to, schemaName) {
|
|
314
|
-
tableName = this.quote(tableName);
|
|
315
|
-
const columnName = this.quote(to.name);
|
|
316
|
-
return `alter table ${tableName} modify ${columnName} ${this.getColumnDeclarationSQL(to)}`;
|
|
317
|
-
}
|
|
318
|
-
alterTableColumn(column, table, changedProperties) {
|
|
319
|
-
const col = this.createTableColumn(column, table, changedProperties);
|
|
320
|
-
return [`alter table ${table.getQuotedName()} modify ${col}`];
|
|
321
|
-
}
|
|
322
|
-
getColumnDeclarationSQL(col) {
|
|
323
|
-
let ret = col.type;
|
|
324
|
-
ret += col.unsigned ? ' unsigned' : '';
|
|
325
|
-
ret += col.autoincrement ? ' auto_increment' : '';
|
|
326
|
-
ret += ' ';
|
|
327
|
-
ret += col.nullable ? 'null' : 'not null';
|
|
328
|
-
ret += col.default ? ' default ' + col.default : '';
|
|
329
|
-
ret += col.comment ? ` comment ${this.platform.quoteValue(col.comment)}` : '';
|
|
330
|
-
return ret;
|
|
286
|
+
Object.keys(ret).forEach(key => {
|
|
287
|
+
const parts = key.split('.');
|
|
288
|
+
/* v8 ignore next */
|
|
289
|
+
const schemaName = parts.length > 1 ? parts[0] : undefined;
|
|
290
|
+
ret[key] = this.mapForeignKeys(ret[key], key, schemaName);
|
|
291
|
+
});
|
|
292
|
+
return ret;
|
|
293
|
+
}
|
|
294
|
+
getPreAlterTable(tableDiff, safe) {
|
|
295
|
+
// Dropping primary keys requires to unset autoincrement attribute on the particular column first.
|
|
296
|
+
const pk = Object.values(tableDiff.removedIndexes).find(idx => idx.primary);
|
|
297
|
+
if (!pk || safe) {
|
|
298
|
+
return [];
|
|
331
299
|
}
|
|
332
|
-
|
|
333
|
-
|
|
300
|
+
return pk.columnNames
|
|
301
|
+
.filter(col => tableDiff.fromTable.hasColumn(col))
|
|
302
|
+
.map(col => tableDiff.fromTable.getColumn(col))
|
|
303
|
+
.filter(col => col.autoincrement)
|
|
304
|
+
.map(
|
|
305
|
+
col =>
|
|
306
|
+
`alter table \`${tableDiff.name}\` modify \`${col.name}\` ${this.getColumnDeclarationSQL({ ...col, autoincrement: false })}`,
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
getRenameColumnSQL(tableName, oldColumnName, to) {
|
|
310
|
+
tableName = this.quote(tableName);
|
|
311
|
+
oldColumnName = this.quote(oldColumnName);
|
|
312
|
+
const columnName = this.quote(to.name);
|
|
313
|
+
return `alter table ${tableName} change ${oldColumnName} ${columnName} ${this.getColumnDeclarationSQL(to)}`;
|
|
314
|
+
}
|
|
315
|
+
getRenameIndexSQL(tableName, index, oldIndexName) {
|
|
316
|
+
tableName = this.quote(tableName);
|
|
317
|
+
oldIndexName = this.quote(oldIndexName);
|
|
318
|
+
const keyName = this.quote(index.keyName);
|
|
319
|
+
return [`alter table ${tableName} rename index ${oldIndexName} to ${keyName}`];
|
|
320
|
+
}
|
|
321
|
+
getChangeColumnCommentSQL(tableName, to, schemaName) {
|
|
322
|
+
tableName = this.quote(tableName);
|
|
323
|
+
const columnName = this.quote(to.name);
|
|
324
|
+
return `alter table ${tableName} modify ${columnName} ${this.getColumnDeclarationSQL(to)}`;
|
|
325
|
+
}
|
|
326
|
+
alterTableColumn(column, table, changedProperties) {
|
|
327
|
+
const col = this.createTableColumn(column, table, changedProperties);
|
|
328
|
+
return [`alter table ${table.getQuotedName()} modify ${col}`];
|
|
329
|
+
}
|
|
330
|
+
getColumnDeclarationSQL(col) {
|
|
331
|
+
let ret = col.type;
|
|
332
|
+
ret += col.unsigned ? ' unsigned' : '';
|
|
333
|
+
ret += col.autoincrement ? ' auto_increment' : '';
|
|
334
|
+
ret += ' ';
|
|
335
|
+
ret += col.nullable ? 'null' : 'not null';
|
|
336
|
+
ret += col.default ? ' default ' + col.default : '';
|
|
337
|
+
ret += col.comment ? ` comment ${this.platform.quoteValue(col.comment)}` : '';
|
|
338
|
+
return ret;
|
|
339
|
+
}
|
|
340
|
+
async getAllEnumDefinitions(connection, tables) {
|
|
341
|
+
const sql = `select column_name as column_name, column_type as column_type, table_name as table_name
|
|
334
342
|
from information_schema.columns
|
|
335
343
|
where data_type = 'enum' and table_name in (${tables.map(t => `'${t.table_name}'`).join(', ')}) and table_schema = database()`;
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
344
|
+
const enums = await connection.execute(sql);
|
|
345
|
+
return enums.reduce((o, item) => {
|
|
346
|
+
o[item.table_name] ??= {};
|
|
347
|
+
o[item.table_name][item.column_name] = item.column_type
|
|
348
|
+
.match(/enum\((.*)\)/)[1]
|
|
349
|
+
.split(',')
|
|
350
|
+
.map(item => /'(.*)'/.exec(item)[1]);
|
|
351
|
+
return o;
|
|
352
|
+
}, {});
|
|
353
|
+
}
|
|
354
|
+
async supportsCheckConstraints(connection) {
|
|
355
|
+
if (this.#cache.supportsCheckConstraints != null) {
|
|
356
|
+
return this.#cache.supportsCheckConstraints;
|
|
345
357
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
return (this.#cache.supportsCheckConstraints = res.length > 0);
|
|
353
|
-
}
|
|
354
|
-
getChecksSQL(tables) {
|
|
355
|
-
return `select cc.constraint_schema as table_schema, tc.table_name as table_name, cc.constraint_name as name, cc.check_clause as expression
|
|
358
|
+
const sql = `select 1 from information_schema.tables where table_name = 'CHECK_CONSTRAINTS' and table_schema = 'information_schema'`;
|
|
359
|
+
const res = await connection.execute(sql);
|
|
360
|
+
return (this.#cache.supportsCheckConstraints = res.length > 0);
|
|
361
|
+
}
|
|
362
|
+
getChecksSQL(tables) {
|
|
363
|
+
return `select cc.constraint_schema as table_schema, tc.table_name as table_name, cc.constraint_name as name, cc.check_clause as expression
|
|
356
364
|
from information_schema.check_constraints cc
|
|
357
365
|
join information_schema.table_constraints tc
|
|
358
366
|
on tc.constraint_schema = cc.constraint_schema
|
|
@@ -360,12 +368,12 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
360
368
|
and constraint_type = 'CHECK'
|
|
361
369
|
where tc.table_name in (${tables.map(t => this.platform.quoteValue(t.table_name))}) and tc.constraint_schema = database()
|
|
362
370
|
order by tc.constraint_name`;
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
+
}
|
|
372
|
+
normalizeDefaultValue(defaultValue, length) {
|
|
373
|
+
return super.normalizeDefaultValue(defaultValue, length, MySqlSchemaHelper.DEFAULT_VALUES);
|
|
374
|
+
}
|
|
375
|
+
wrap(val, type) {
|
|
376
|
+
const stringType = type instanceof StringType || type instanceof TextType || type instanceof EnumType;
|
|
377
|
+
return typeof val === 'string' && val.length > 0 && stringType ? this.platform.quoteValue(val) : val;
|
|
378
|
+
}
|
|
371
379
|
}
|