@mikro-orm/mssql 7.0.15-dev.9 → 7.0.15
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/MsSqlConnection.d.ts +4 -4
- package/MsSqlConnection.js +72 -69
- package/MsSqlDriver.d.ts +30 -7
- package/MsSqlDriver.js +74 -71
- package/MsSqlExceptionConverter.d.ts +5 -5
- package/MsSqlExceptionConverter.js +44 -33
- package/MsSqlMikroORM.d.ts +50 -12
- package/MsSqlMikroORM.js +14 -14
- package/MsSqlPlatform.d.ts +85 -68
- package/MsSqlPlatform.js +239 -224
- package/MsSqlQueryBuilder.d.ts +10 -3
- package/MsSqlQueryBuilder.js +16 -16
- package/MsSqlSchemaGenerator.d.ts +3 -3
- package/MsSqlSchemaGenerator.js +22 -22
- package/MsSqlSchemaHelper.d.ts +98 -52
- package/MsSqlSchemaHelper.js +552 -535
- package/README.md +1 -1
- package/UnicodeCharacterType.d.ts +2 -2
- package/UnicodeCharacterType.js +7 -7
- package/UnicodeStringType.d.ts +17 -14
- package/UnicodeStringType.js +42 -42
- package/index.d.ts +5 -1
- package/index.js +1 -1
- package/package.json +4 -4
package/MsSqlSchemaHelper.js
CHANGED
|
@@ -1,96 +1,98 @@
|
|
|
1
|
-
import { EnumType, SchemaHelper, StringType, TextType, Utils
|
|
1
|
+
import { EnumType, SchemaHelper, StringType, TextType, Utils } from '@mikro-orm/sql';
|
|
2
2
|
import { UnicodeStringType } from './UnicodeStringType.js';
|
|
3
3
|
/** Schema introspection helper for Microsoft SQL Server. */
|
|
4
4
|
export class MsSqlSchemaHelper extends SchemaHelper {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
5
|
+
static DEFAULT_VALUES = {
|
|
6
|
+
true: ['1'],
|
|
7
|
+
false: ['0'],
|
|
8
|
+
'getdate()': ['current_timestamp'],
|
|
9
|
+
};
|
|
10
|
+
getManagementDbName() {
|
|
11
|
+
return 'master';
|
|
12
|
+
}
|
|
13
|
+
getDropDatabaseSQL(name) {
|
|
14
|
+
// `set offline` rejects all connections including the issuing session, so there is no
|
|
15
|
+
// single-user race window where a torn-down pool connection can reconnect between the
|
|
16
|
+
// mode switch and the drop (SQL Server error 3702 "currently in use"). Dropping an
|
|
17
|
+
// offline database leaves the underlying `.mdf`/`.ldf` files behind though, which
|
|
18
|
+
// makes a subsequent `create database` with the same name fail with error 5170
|
|
19
|
+
// ("file already exists"). Capture the physical paths up front and call
|
|
20
|
+
// `master.sys.xp_delete_files` after the drop to clean them up.
|
|
21
|
+
const quoted = this.quote(name);
|
|
22
|
+
const literal = this.platform.quoteValue(name);
|
|
23
|
+
return (
|
|
24
|
+
`if db_id(${literal}) is not null begin ` +
|
|
25
|
+
`declare @drop_files table (path nvarchar(260)); ` +
|
|
26
|
+
`insert into @drop_files (path) select physical_name from sys.master_files where database_id = db_id(${literal}); ` +
|
|
27
|
+
`alter database ${quoted} set offline with rollback immediate; ` +
|
|
28
|
+
`drop database ${quoted}; ` +
|
|
29
|
+
`declare @drop_path nvarchar(260); ` +
|
|
30
|
+
`declare drop_files_cursor cursor local fast_forward for select path from @drop_files; ` +
|
|
31
|
+
`open drop_files_cursor; fetch next from drop_files_cursor into @drop_path; ` +
|
|
32
|
+
`while @@fetch_status = 0 begin ` +
|
|
33
|
+
`begin try exec master.sys.xp_delete_files @drop_path; end try begin catch end catch; ` +
|
|
34
|
+
`fetch next from drop_files_cursor into @drop_path; ` +
|
|
35
|
+
`end ` +
|
|
36
|
+
`close drop_files_cursor; deallocate drop_files_cursor; ` +
|
|
37
|
+
`end`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
disableForeignKeysSQL() {
|
|
41
|
+
return `exec sp_MSforeachtable 'alter table ? nocheck constraint all';`;
|
|
42
|
+
}
|
|
43
|
+
enableForeignKeysSQL() {
|
|
44
|
+
return `exec sp_MSforeachtable 'alter table ? check constraint all';`;
|
|
45
|
+
}
|
|
46
|
+
getDatabaseExistsSQL(name) {
|
|
47
|
+
return `select 1 from sys.databases where name = N'${name}'`;
|
|
48
|
+
}
|
|
49
|
+
getListTablesSQL() {
|
|
50
|
+
return `select t.name as table_name, schema_name(t2.schema_id) schema_name, ep.value as table_comment
|
|
49
51
|
from sysobjects t
|
|
50
52
|
inner join sys.tables t2 on t2.object_id = t.id
|
|
51
53
|
left join sys.extended_properties ep on ep.major_id = t.id and ep.name = 'MS_Description' and ep.minor_id = 0
|
|
52
54
|
order by schema_name(t2.schema_id), t.name`;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
}
|
|
56
|
+
getListViewsSQL() {
|
|
57
|
+
return `select v.name as view_name, schema_name(v.schema_id) as schema_name, m.definition as view_definition
|
|
56
58
|
from sys.views v
|
|
57
59
|
inner join sys.sql_modules m on v.object_id = m.object_id
|
|
58
60
|
order by schema_name(v.schema_id), v.name`;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
61
|
+
}
|
|
62
|
+
async loadViews(schema, connection, schemaName, ctx) {
|
|
63
|
+
const views = await connection.execute(this.getListViewsSQL(), [], 'all', ctx);
|
|
64
|
+
for (const view of views) {
|
|
65
|
+
// Extract SELECT statement from CREATE VIEW ... AS SELECT ...
|
|
66
|
+
const match = /\bAS\s+(.+)$/is.exec(view.view_definition);
|
|
67
|
+
const definition = match?.[1]?.trim();
|
|
68
|
+
if (definition) {
|
|
69
|
+
const schemaName = view.schema_name === this.platform.getDefaultSchemaName() ? undefined : view.schema_name;
|
|
70
|
+
schema.addView(view.view_name, schemaName, definition);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async getNamespaces(connection, ctx) {
|
|
75
|
+
const sql = `select name as schema_name from sys.schemas order by name`;
|
|
76
|
+
const res = await connection.execute(sql, [], 'all', ctx);
|
|
77
|
+
return res.map(row => row.schema_name);
|
|
78
|
+
}
|
|
79
|
+
normalizeDefaultValue(defaultValue, length, defaultValues = {}, stripQuotes = false) {
|
|
80
|
+
let match = /^\((.*)\)$/.exec(defaultValue);
|
|
81
|
+
if (match) {
|
|
82
|
+
defaultValue = match[1];
|
|
83
|
+
}
|
|
84
|
+
match = /^\((.*)\)$/.exec(defaultValue);
|
|
85
|
+
if (match) {
|
|
86
|
+
defaultValue = match[1];
|
|
87
|
+
}
|
|
88
|
+
match = /^'(.*)'$/.exec(defaultValue);
|
|
89
|
+
if (stripQuotes && match) {
|
|
90
|
+
defaultValue = match[1];
|
|
91
|
+
}
|
|
92
|
+
return super.normalizeDefaultValue(defaultValue, length, MsSqlSchemaHelper.DEFAULT_VALUES);
|
|
93
|
+
}
|
|
94
|
+
async getAllColumns(connection, tablesBySchemas, ctx) {
|
|
95
|
+
const sql = `select table_name as table_name,
|
|
94
96
|
table_schema as schema_name,
|
|
95
97
|
column_name as column_name,
|
|
96
98
|
column_default as column_default,
|
|
@@ -112,57 +114,57 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
112
114
|
left join sys.default_constraints t5 on sc.default_object_id = t5.object_id
|
|
113
115
|
where (${[...tablesBySchemas.entries()].map(([schema, tables]) => `(ic.table_name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(',')}) and ic.table_schema = '${schema}')`).join(' or ')})
|
|
114
116
|
order by ordinal_position`;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
117
|
+
const allColumns = await connection.execute(sql, [], 'all', ctx);
|
|
118
|
+
const str = val => (val != null ? '' + val : val);
|
|
119
|
+
const ret = {};
|
|
120
|
+
for (const col of allColumns) {
|
|
121
|
+
const mappedType = this.platform.getMappedType(col.data_type);
|
|
122
|
+
const defaultValue = str(this.normalizeDefaultValue(col.column_default, col.length, {}, true));
|
|
123
|
+
const increments = col.is_identity === 1 && connection.getPlatform().isNumericColumn(mappedType);
|
|
124
|
+
const key = this.getTableKey(col);
|
|
125
|
+
/* v8 ignore next */
|
|
126
|
+
const generated = col.generation_expression
|
|
127
|
+
? `${col.generation_expression}${col.is_persisted ? ' persisted' : ''}`
|
|
128
|
+
: undefined;
|
|
129
|
+
let type = col.data_type;
|
|
130
|
+
if (['varchar', 'nvarchar', 'char', 'nchar', 'varbinary'].includes(col.data_type)) {
|
|
131
|
+
col.length = col.character_maximum_length;
|
|
132
|
+
}
|
|
133
|
+
if (['timestamp', 'datetime', 'datetime2', 'time', 'datetimeoffset'].includes(col.data_type)) {
|
|
134
|
+
col.length = col.datetime_precision;
|
|
135
|
+
}
|
|
136
|
+
if (col.length != null && !type.endsWith(`(${col.length})`) && !['text', 'date'].includes(type)) {
|
|
137
|
+
type += `(${col.length === -1 ? 'max' : col.length})`;
|
|
138
|
+
}
|
|
139
|
+
if (type === 'numeric' && col.numeric_precision != null && col.numeric_scale != null) {
|
|
140
|
+
type += `(${col.numeric_precision},${col.numeric_scale})`;
|
|
141
|
+
}
|
|
142
|
+
if (type === 'float' && col.numeric_precision != null) {
|
|
143
|
+
type += `(${col.numeric_precision})`;
|
|
144
|
+
}
|
|
145
|
+
ret[key] ??= [];
|
|
146
|
+
ret[key].push({
|
|
147
|
+
name: col.column_name,
|
|
148
|
+
type: this.platform.isNumericColumn(mappedType)
|
|
149
|
+
? col.data_type.replace(/ unsigned$/, '').replace(/\(\d+\)$/, '')
|
|
150
|
+
: type,
|
|
151
|
+
mappedType,
|
|
152
|
+
unsigned: col.data_type.endsWith(' unsigned'),
|
|
153
|
+
length: col.length,
|
|
154
|
+
default: this.wrap(defaultValue, mappedType),
|
|
155
|
+
defaultConstraint: col.column_default_name,
|
|
156
|
+
nullable: col.is_nullable === 'YES',
|
|
157
|
+
autoincrement: increments,
|
|
158
|
+
precision: col.numeric_precision,
|
|
159
|
+
scale: col.numeric_scale,
|
|
160
|
+
comment: col.column_comment,
|
|
161
|
+
generated,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return ret;
|
|
165
|
+
}
|
|
166
|
+
async getAllIndexes(connection, tablesBySchemas, ctx) {
|
|
167
|
+
const sql = `select t.name as table_name,
|
|
166
168
|
ind.name as index_name,
|
|
167
169
|
is_unique as is_unique,
|
|
168
170
|
ind.is_primary_key as is_primary_key,
|
|
@@ -181,60 +183,60 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
181
183
|
where
|
|
182
184
|
(${[...tablesBySchemas.entries()].map(([schema, tables]) => `(t.name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(',')}) and schema_name(t.schema_id) = '${schema}')`).join(' OR ')})
|
|
183
185
|
order by t.name, ind.name, ic.is_included_column, ic.key_ordinal`;
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
186
|
+
const allIndexes = await connection.execute(sql, [], 'all', ctx);
|
|
187
|
+
const ret = {};
|
|
188
|
+
for (const index of allIndexes) {
|
|
189
|
+
const key = this.getTableKey(index);
|
|
190
|
+
const isIncluded = index.is_included_column;
|
|
191
|
+
const indexDef = {
|
|
192
|
+
columnNames: isIncluded ? [] : [index.column_name],
|
|
193
|
+
keyName: index.index_name,
|
|
194
|
+
unique: index.is_unique,
|
|
195
|
+
primary: index.is_primary_key,
|
|
196
|
+
constraint: index.is_unique,
|
|
197
|
+
};
|
|
198
|
+
// Capture INCLUDE columns
|
|
199
|
+
if (isIncluded) {
|
|
200
|
+
indexDef.include = [index.column_name];
|
|
201
|
+
}
|
|
202
|
+
// Capture sort order for key columns
|
|
203
|
+
if (!isIncluded && index.is_descending_key) {
|
|
204
|
+
indexDef.columns = [{ name: index.column_name, sort: 'DESC' }];
|
|
205
|
+
}
|
|
206
|
+
// Capture disabled flag
|
|
207
|
+
if (index.is_disabled) {
|
|
208
|
+
indexDef.disabled = true;
|
|
209
|
+
}
|
|
210
|
+
// Capture clustered flag (type 1 = clustered)
|
|
211
|
+
if (index.index_type === 1 && !index.is_primary_key) {
|
|
212
|
+
indexDef.clustered = true;
|
|
213
|
+
}
|
|
214
|
+
// Capture fill factor (0 means default, so only set if non-zero)
|
|
215
|
+
if (index.fill_factor > 0) {
|
|
216
|
+
indexDef.fillFactor = index.fill_factor;
|
|
217
|
+
}
|
|
218
|
+
if (index.column_name?.match(/[(): ,"'`]/) || index.expression?.match(/where /i)) {
|
|
219
|
+
indexDef.expression = index.expression; // required for the `getCreateIndexSQL()` call
|
|
220
|
+
indexDef.expression = this.getCreateIndexSQL(index.table_name, indexDef, !!index.expression);
|
|
221
|
+
}
|
|
222
|
+
ret[key] ??= [];
|
|
223
|
+
ret[key].push(indexDef);
|
|
224
|
+
}
|
|
225
|
+
for (const key of Object.keys(ret)) {
|
|
226
|
+
ret[key] = await this.mapIndexes(ret[key]);
|
|
227
|
+
}
|
|
228
|
+
return ret;
|
|
229
|
+
}
|
|
230
|
+
mapForeignKeys(fks, tableName, schemaName) {
|
|
231
|
+
const ret = super.mapForeignKeys(fks, tableName, schemaName);
|
|
232
|
+
for (const fk of Utils.values(ret)) {
|
|
233
|
+
fk.columnNames = Utils.unique(fk.columnNames);
|
|
234
|
+
fk.referencedColumnNames = Utils.unique(fk.referencedColumnNames);
|
|
235
|
+
}
|
|
236
|
+
return ret;
|
|
237
|
+
}
|
|
238
|
+
async getAllForeignKeys(connection, tablesBySchemas, ctx) {
|
|
239
|
+
const sql = `select ccu.constraint_name, ccu.table_name, ccu.table_schema schema_name, ccu.column_name,
|
|
238
240
|
kcu.constraint_schema referenced_schema_name,
|
|
239
241
|
kcu.column_name referenced_column_name,
|
|
240
242
|
kcu.table_name referenced_table_name,
|
|
@@ -245,40 +247,40 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
245
247
|
inner join information_schema.key_column_usage kcu on kcu.constraint_name = rc.unique_constraint_name and rc.unique_constraint_schema = kcu.constraint_schema
|
|
246
248
|
where (${[...tablesBySchemas.entries()].map(([schema, tables]) => `(ccu.table_name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(',')}) and ccu.table_schema = '${schema}')`).join(' or ')})
|
|
247
249
|
order by kcu.table_schema, kcu.table_name, kcu.ordinal_position, kcu.constraint_name`;
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
250
|
+
const allFks = await connection.execute(sql, [], 'all', ctx);
|
|
251
|
+
const ret = {};
|
|
252
|
+
for (const fk of allFks) {
|
|
253
|
+
const key = this.getTableKey(fk);
|
|
254
|
+
ret[key] ??= [];
|
|
255
|
+
ret[key].push(fk);
|
|
256
|
+
}
|
|
257
|
+
Object.keys(ret).forEach(key => {
|
|
258
|
+
const [schemaName, tableName] = key.split('.');
|
|
259
|
+
ret[key] = this.mapForeignKeys(ret[key], tableName, schemaName);
|
|
260
|
+
});
|
|
261
|
+
return ret;
|
|
262
|
+
}
|
|
263
|
+
getEnumDefinitions(checks) {
|
|
264
|
+
return checks.reduce((o, item, index) => {
|
|
265
|
+
// check constraints are defined as
|
|
266
|
+
// `([type]='owner' OR [type]='manager' OR [type]='employee')`
|
|
267
|
+
const m1 = item.definition?.match(/^check \((.*)\)/);
|
|
268
|
+
let items = m1?.[1].split(' OR ');
|
|
269
|
+
/* v8 ignore next */
|
|
270
|
+
const hasItems = (items?.length ?? 0) > 0;
|
|
271
|
+
if (item.columnName && hasItems) {
|
|
272
|
+
items = items
|
|
273
|
+
.map(val => /^\(?'(.*)'/.exec(val.trim().replace(`[${item.columnName}]=`, ''))?.[1])
|
|
274
|
+
.filter(Boolean);
|
|
275
|
+
if (items.length > 0) {
|
|
276
|
+
o[item.columnName] = items.reverse();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return o;
|
|
280
|
+
}, {});
|
|
281
|
+
}
|
|
282
|
+
getChecksSQL(tablesBySchemas) {
|
|
283
|
+
return `select con.name as name,
|
|
282
284
|
schema_name(t.schema_id) schema_name,
|
|
283
285
|
t.name table_name,
|
|
284
286
|
col.name column_name,
|
|
@@ -288,324 +290,339 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
288
290
|
left outer join sys.all_columns col on con.parent_column_id = col.column_id and con.parent_object_id = col.object_id
|
|
289
291
|
where (${[...tablesBySchemas.entries()].map(([schema, tables]) => `t.name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(',')}) and schema_name(t.schema_id) = '${schema}'`).join(' or ')})
|
|
290
292
|
order by con.name`;
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
return ` with (${withOptions.join(', ')})`;
|
|
528
|
-
}
|
|
529
|
-
return '';
|
|
530
|
-
}
|
|
531
|
-
createIndex(index, table, createPrimary = false) {
|
|
532
|
-
if (index.primary) {
|
|
533
|
-
return '';
|
|
534
|
-
}
|
|
535
|
-
if (index.expression) {
|
|
536
|
-
return index.expression;
|
|
537
|
-
}
|
|
538
|
-
const needsWhereClause = index.unique && index.columnNames.some(column => table.getColumn(column)?.nullable);
|
|
539
|
-
if (!needsWhereClause) {
|
|
540
|
-
return this.getCreateIndexSQL(table.getShortestName(), index);
|
|
541
|
-
}
|
|
542
|
-
// Generate without disabled suffix, insert WHERE clause, then re-add disabled
|
|
543
|
-
let sql = this.getCreateIndexSQL(table.getShortestName(), { ...index, disabled: false });
|
|
544
|
-
sql += ' where ' + index.columnNames.map(c => `${this.quote(c)} is not null`).join(' and ');
|
|
545
|
-
if (index.disabled) {
|
|
546
|
-
sql += `;\nalter index ${this.quote(index.keyName)} on ${table.getQuotedName()} disable`;
|
|
547
|
-
}
|
|
548
|
-
return sql;
|
|
549
|
-
}
|
|
550
|
-
dropForeignKey(tableName, constraintName) {
|
|
551
|
-
return `alter table ${this.quote(tableName)} drop constraint ${this.quote(constraintName)}`;
|
|
552
|
-
}
|
|
553
|
-
dropTableIfExists(name, schema) {
|
|
554
|
-
if (schema === this.platform.getDefaultSchemaName()) {
|
|
555
|
-
schema = undefined;
|
|
556
|
-
}
|
|
557
|
-
return `if object_id('${this.quote(schema, name)}', 'U') is not null drop table ${this.quote(schema, name)}`;
|
|
558
|
-
}
|
|
559
|
-
dropViewIfExists(name, schema) {
|
|
560
|
-
const viewName = this.quote(this.getTableName(name, schema));
|
|
561
|
-
return `if object_id('${viewName}', 'V') is not null drop view ${viewName}`;
|
|
562
|
-
}
|
|
563
|
-
getAddColumnsSQL(table, columns) {
|
|
564
|
-
const adds = columns
|
|
565
|
-
.map(column => {
|
|
566
|
-
return this.createTableColumn(column, table);
|
|
293
|
+
}
|
|
294
|
+
async getAllChecks(connection, tablesBySchemas, ctx) {
|
|
295
|
+
const sql = this.getChecksSQL(tablesBySchemas);
|
|
296
|
+
const allChecks = await connection.execute(sql, [], 'all', ctx);
|
|
297
|
+
const ret = {};
|
|
298
|
+
for (const check of allChecks) {
|
|
299
|
+
const key = this.getTableKey(check);
|
|
300
|
+
ret[key] ??= [];
|
|
301
|
+
const expression = check.expression.replace(/^\((.*)\)$/, '$1');
|
|
302
|
+
ret[key].push({
|
|
303
|
+
name: check.name,
|
|
304
|
+
columnName: check.column_name,
|
|
305
|
+
definition: `check (${expression})`,
|
|
306
|
+
expression,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
return ret;
|
|
310
|
+
}
|
|
311
|
+
async loadInformationSchema(schema, connection, tables, schemas, ctx) {
|
|
312
|
+
if (tables.length === 0) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
const tablesBySchema = this.getTablesGroupedBySchemas(tables);
|
|
316
|
+
const columns = await this.getAllColumns(connection, tablesBySchema, ctx);
|
|
317
|
+
const indexes = await this.getAllIndexes(connection, tablesBySchema, ctx);
|
|
318
|
+
const checks = await this.getAllChecks(connection, tablesBySchema, ctx);
|
|
319
|
+
const fks = await this.getAllForeignKeys(connection, tablesBySchema, ctx);
|
|
320
|
+
for (const t of tables) {
|
|
321
|
+
const key = this.getTableKey(t);
|
|
322
|
+
const table = schema.addTable(t.table_name, t.schema_name, t.table_comment);
|
|
323
|
+
const pks = await this.getPrimaryKeys(connection, indexes[key], table.name, table.schema);
|
|
324
|
+
const enums = this.getEnumDefinitions(checks[key] ?? []);
|
|
325
|
+
table.init(columns[key], indexes[key], checks[key], pks, fks[key], enums);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
getPreAlterTable(tableDiff, safe) {
|
|
329
|
+
const ret = [];
|
|
330
|
+
const indexes = tableDiff.fromTable.getIndexes();
|
|
331
|
+
const parts = tableDiff.name.split('.');
|
|
332
|
+
const tableName = parts.pop();
|
|
333
|
+
const schemaName = parts.pop();
|
|
334
|
+
/* v8 ignore next */
|
|
335
|
+
const name =
|
|
336
|
+
(schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName;
|
|
337
|
+
const quotedName = this.quote(name);
|
|
338
|
+
// indexes need to be first dropped to be able to change a column type
|
|
339
|
+
const changedTypes = Object.values(tableDiff.changedColumns).filter(col => col.changedProperties.has('type'));
|
|
340
|
+
for (const col of changedTypes) {
|
|
341
|
+
for (const index of indexes) {
|
|
342
|
+
if (index.columnNames.includes(col.column.name)) {
|
|
343
|
+
ret.push(this.getDropIndexSQL(name, index));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// convert to string first if it's not already a string or has a smaller length
|
|
347
|
+
const type = this.platform.extractSimpleType(col.fromColumn.type);
|
|
348
|
+
if (!['varchar', 'nvarchar', 'varbinary'].includes(type) || col.fromColumn.length < col.column.length) {
|
|
349
|
+
ret.push(`alter table ${quotedName} alter column [${col.oldColumnName}] nvarchar(max)`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return ret;
|
|
353
|
+
}
|
|
354
|
+
getPostAlterTable(tableDiff, safe) {
|
|
355
|
+
const ret = [];
|
|
356
|
+
const indexes = tableDiff.fromTable.getIndexes();
|
|
357
|
+
const parts = tableDiff.name.split('.');
|
|
358
|
+
const tableName = parts.pop();
|
|
359
|
+
const schemaName = parts.pop();
|
|
360
|
+
/* v8 ignore next */
|
|
361
|
+
const name =
|
|
362
|
+
(schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName;
|
|
363
|
+
// indexes need to be first dropped to be able to change a column type
|
|
364
|
+
const changedTypes = Object.values(tableDiff.changedColumns).filter(col => col.changedProperties.has('type'));
|
|
365
|
+
for (const col of changedTypes) {
|
|
366
|
+
for (const index of indexes) {
|
|
367
|
+
if (index.columnNames.includes(col.column.name)) {
|
|
368
|
+
this.append(ret, this.getCreateIndexSQL(name, index));
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return ret;
|
|
373
|
+
}
|
|
374
|
+
getCreateNamespaceSQL(name) {
|
|
375
|
+
return `if (schema_id(${this.platform.quoteValue(name)}) is null) begin exec ('create schema ${this.quote(name)} authorization [dbo]') end`;
|
|
376
|
+
}
|
|
377
|
+
getDropNamespaceSQL(name) {
|
|
378
|
+
return `drop schema if exists ${this.quote(name)}`;
|
|
379
|
+
}
|
|
380
|
+
getDropIndexSQL(tableName, index) {
|
|
381
|
+
return `drop index ${this.quote(index.keyName)} on ${this.quote(tableName)}`;
|
|
382
|
+
}
|
|
383
|
+
dropIndex(table, index, oldIndexName = index.keyName) {
|
|
384
|
+
if (index.primary) {
|
|
385
|
+
return `alter table ${this.quote(table)} drop constraint ${this.quote(oldIndexName)}`;
|
|
386
|
+
}
|
|
387
|
+
return `drop index ${this.quote(oldIndexName)} on ${this.quote(table)}`;
|
|
388
|
+
}
|
|
389
|
+
getDropColumnsSQL(tableName, columns, schemaName) {
|
|
390
|
+
/* v8 ignore next */
|
|
391
|
+
const tableNameRaw = this.quote(
|
|
392
|
+
(schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName,
|
|
393
|
+
);
|
|
394
|
+
const drops = [];
|
|
395
|
+
const constraints = this.getDropDefaultsSQL(tableName, columns, schemaName);
|
|
396
|
+
for (const column of columns) {
|
|
397
|
+
drops.push(this.quote(column.name));
|
|
398
|
+
}
|
|
399
|
+
return `${constraints.join(';\n')};\nalter table ${tableNameRaw} drop column ${drops.join(', ')}`;
|
|
400
|
+
}
|
|
401
|
+
getDropDefaultsSQL(tableName, columns, schemaName) {
|
|
402
|
+
/* v8 ignore next */
|
|
403
|
+
const tableNameRaw = this.quote(
|
|
404
|
+
(schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName,
|
|
405
|
+
);
|
|
406
|
+
const constraints = [];
|
|
407
|
+
schemaName ??= this.platform.getDefaultSchemaName();
|
|
408
|
+
for (const column of columns) {
|
|
409
|
+
if (column.defaultConstraint) {
|
|
410
|
+
constraints.push(`alter table ${tableNameRaw} drop constraint ${this.quote(column.defaultConstraint)}`);
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
const i = globalThis.idx;
|
|
414
|
+
globalThis.idx++;
|
|
415
|
+
constraints.push(
|
|
416
|
+
`declare @constraint${i} varchar(100) = (select default_constraints.name from sys.all_columns` +
|
|
417
|
+
' join sys.tables on all_columns.object_id = tables.object_id' +
|
|
418
|
+
' join sys.schemas on tables.schema_id = schemas.schema_id' +
|
|
419
|
+
' join sys.default_constraints on all_columns.default_object_id = default_constraints.object_id' +
|
|
420
|
+
` where schemas.name = '${schemaName}' and tables.name = '${tableName}' and all_columns.name = '${column.name}')` +
|
|
421
|
+
` if @constraint${i} is not null exec('alter table ${tableNameRaw} drop constraint ' + @constraint${i})`,
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
return constraints;
|
|
425
|
+
}
|
|
426
|
+
getRenameColumnSQL(tableName, oldColumnName, to, schemaName) {
|
|
427
|
+
/* v8 ignore next */
|
|
428
|
+
const oldName =
|
|
429
|
+
(schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') +
|
|
430
|
+
tableName +
|
|
431
|
+
'.' +
|
|
432
|
+
oldColumnName;
|
|
433
|
+
const columnName = this.platform.quoteValue(to.name);
|
|
434
|
+
return `exec sp_rename ${this.platform.quoteValue(oldName)}, ${columnName}, 'COLUMN'`;
|
|
435
|
+
}
|
|
436
|
+
createTableColumn(column, table, changedProperties) {
|
|
437
|
+
const compositePK = table.getPrimaryKey()?.composite;
|
|
438
|
+
const primaryKey = !changedProperties && !this.hasNonDefaultPrimaryKeyName(table);
|
|
439
|
+
const columnType = column.generated ? `as ${column.generated}` : column.type;
|
|
440
|
+
const col = [this.quote(column.name)];
|
|
441
|
+
if (
|
|
442
|
+
column.autoincrement &&
|
|
443
|
+
!column.generated &&
|
|
444
|
+
!compositePK &&
|
|
445
|
+
(!changedProperties || changedProperties.has('autoincrement') || changedProperties.has('type'))
|
|
446
|
+
) {
|
|
447
|
+
col.push(column.mappedType.getColumnType({ autoincrement: true }, this.platform));
|
|
448
|
+
} else {
|
|
449
|
+
col.push(columnType);
|
|
450
|
+
}
|
|
451
|
+
Utils.runIfNotEmpty(() => col.push('identity(1,1)'), column.autoincrement);
|
|
452
|
+
Utils.runIfNotEmpty(() => col.push('null'), column.nullable);
|
|
453
|
+
Utils.runIfNotEmpty(() => col.push('not null'), !column.nullable && !column.generated);
|
|
454
|
+
if (
|
|
455
|
+
column.autoincrement &&
|
|
456
|
+
!column.generated &&
|
|
457
|
+
!compositePK &&
|
|
458
|
+
(!changedProperties || changedProperties.has('autoincrement') || changedProperties.has('type'))
|
|
459
|
+
) {
|
|
460
|
+
Utils.runIfNotEmpty(() => col.push('primary key'), primaryKey && column.primary);
|
|
461
|
+
}
|
|
462
|
+
const useDefault = changedProperties
|
|
463
|
+
? false
|
|
464
|
+
: column.default != null && column.default !== 'null' && !column.autoincrement;
|
|
465
|
+
const defaultName = this.platform.getConfig().getNamingStrategy().indexName(table.name, [column.name], 'default');
|
|
466
|
+
Utils.runIfNotEmpty(() => col.push(`constraint ${this.quote(defaultName)} default ${column.default}`), useDefault);
|
|
467
|
+
return col.join(' ');
|
|
468
|
+
}
|
|
469
|
+
alterTableColumn(column, table, changedProperties) {
|
|
470
|
+
const parts = [];
|
|
471
|
+
if (changedProperties.has('default')) {
|
|
472
|
+
const [constraint] = this.getDropDefaultsSQL(table.name, [column], table.schema);
|
|
473
|
+
parts.push(constraint);
|
|
474
|
+
}
|
|
475
|
+
if (changedProperties.has('type') || changedProperties.has('nullable')) {
|
|
476
|
+
const col = this.createTableColumn(column, table, changedProperties);
|
|
477
|
+
parts.push(`alter table ${table.getQuotedName()} alter column ${col}`);
|
|
478
|
+
}
|
|
479
|
+
if (changedProperties.has('default') && column.default != null) {
|
|
480
|
+
const defaultName = this.platform.getConfig().getNamingStrategy().indexName(table.name, [column.name], 'default');
|
|
481
|
+
parts.push(
|
|
482
|
+
`alter table ${table.getQuotedName()} add constraint ${this.quote(defaultName)} default ${column.default} for ${this.quote(column.name)}`,
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
return parts;
|
|
486
|
+
}
|
|
487
|
+
getCreateIndexSQL(tableName, index, partialExpression = false) {
|
|
488
|
+
/* v8 ignore next */
|
|
489
|
+
if (index.expression && !partialExpression) {
|
|
490
|
+
return index.expression;
|
|
491
|
+
}
|
|
492
|
+
if (index.fillFactor != null && (index.fillFactor < 0 || index.fillFactor > 100)) {
|
|
493
|
+
throw new Error(`fillFactor must be between 0 and 100, got ${index.fillFactor} for index '${index.keyName}'`);
|
|
494
|
+
}
|
|
495
|
+
const keyName = this.quote(index.keyName);
|
|
496
|
+
// Only add clustered keyword when explicitly requested, otherwise omit (defaults to nonclustered)
|
|
497
|
+
const clustered = index.clustered ? 'clustered ' : '';
|
|
498
|
+
let sql = `create ${index.unique ? 'unique ' : ''}${clustered}index ${keyName} on ${this.quote(tableName)} `;
|
|
499
|
+
if (index.expression && partialExpression) {
|
|
500
|
+
return sql + `(${index.expression})` + this.getMsSqlIndexSuffix(index);
|
|
501
|
+
}
|
|
502
|
+
// Build column list with advanced options
|
|
503
|
+
const columns = this.getIndexColumns(index);
|
|
504
|
+
sql += `(${columns})`;
|
|
505
|
+
// Add INCLUDE clause for covering indexes
|
|
506
|
+
if (index.include?.length) {
|
|
507
|
+
sql += ` include (${index.include.map(c => this.quote(c)).join(', ')})`;
|
|
508
|
+
}
|
|
509
|
+
sql += this.getMsSqlIndexSuffix(index);
|
|
510
|
+
// Disabled indexes need to be created first, then disabled
|
|
511
|
+
if (index.disabled) {
|
|
512
|
+
sql += `;\nalter index ${keyName} on ${this.quote(tableName)} disable`;
|
|
513
|
+
}
|
|
514
|
+
return sql;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Build the column list for a MSSQL index.
|
|
518
|
+
*/
|
|
519
|
+
getIndexColumns(index) {
|
|
520
|
+
if (index.columns?.length) {
|
|
521
|
+
return index.columns
|
|
522
|
+
.map(col => {
|
|
523
|
+
let colDef = this.quote(col.name);
|
|
524
|
+
// MSSQL supports sort order
|
|
525
|
+
if (col.sort) {
|
|
526
|
+
colDef += ` ${col.sort}`;
|
|
527
|
+
}
|
|
528
|
+
return colDef;
|
|
567
529
|
})
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
530
|
+
.join(', ');
|
|
531
|
+
}
|
|
532
|
+
return index.columnNames.map(c => this.quote(c)).join(', ');
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Get MSSQL-specific index WITH options like fill factor.
|
|
536
|
+
*/
|
|
537
|
+
getMsSqlIndexSuffix(index) {
|
|
538
|
+
const withOptions = [];
|
|
539
|
+
if (index.fillFactor != null) {
|
|
540
|
+
withOptions.push(`fillfactor = ${index.fillFactor}`);
|
|
541
|
+
}
|
|
542
|
+
if (withOptions.length > 0) {
|
|
543
|
+
return ` with (${withOptions.join(', ')})`;
|
|
544
|
+
}
|
|
545
|
+
return '';
|
|
546
|
+
}
|
|
547
|
+
createIndex(index, table, createPrimary = false) {
|
|
548
|
+
if (index.primary) {
|
|
549
|
+
return '';
|
|
550
|
+
}
|
|
551
|
+
if (index.expression) {
|
|
552
|
+
return index.expression;
|
|
553
|
+
}
|
|
554
|
+
const needsWhereClause = index.unique && index.columnNames.some(column => table.getColumn(column)?.nullable);
|
|
555
|
+
if (!needsWhereClause) {
|
|
556
|
+
return this.getCreateIndexSQL(table.getShortestName(), index);
|
|
557
|
+
}
|
|
558
|
+
// Generate without disabled suffix, insert WHERE clause, then re-add disabled
|
|
559
|
+
let sql = this.getCreateIndexSQL(table.getShortestName(), { ...index, disabled: false });
|
|
560
|
+
sql += ' where ' + index.columnNames.map(c => `${this.quote(c)} is not null`).join(' and ');
|
|
561
|
+
if (index.disabled) {
|
|
562
|
+
sql += `;\nalter index ${this.quote(index.keyName)} on ${table.getQuotedName()} disable`;
|
|
563
|
+
}
|
|
564
|
+
return sql;
|
|
565
|
+
}
|
|
566
|
+
dropForeignKey(tableName, constraintName) {
|
|
567
|
+
return `alter table ${this.quote(tableName)} drop constraint ${this.quote(constraintName)}`;
|
|
568
|
+
}
|
|
569
|
+
dropTableIfExists(name, schema) {
|
|
570
|
+
if (schema === this.platform.getDefaultSchemaName()) {
|
|
571
|
+
schema = undefined;
|
|
572
|
+
}
|
|
573
|
+
return `if object_id('${this.quote(schema, name)}', 'U') is not null drop table ${this.quote(schema, name)}`;
|
|
574
|
+
}
|
|
575
|
+
dropViewIfExists(name, schema) {
|
|
576
|
+
const viewName = this.quote(this.getTableName(name, schema));
|
|
577
|
+
return `if object_id('${viewName}', 'V') is not null drop view ${viewName}`;
|
|
578
|
+
}
|
|
579
|
+
getAddColumnsSQL(table, columns) {
|
|
580
|
+
const adds = columns
|
|
581
|
+
.map(column => {
|
|
582
|
+
return this.createTableColumn(column, table);
|
|
583
|
+
})
|
|
584
|
+
.join(', ');
|
|
585
|
+
return [`alter table ${table.getQuotedName()} add ${adds}`];
|
|
586
|
+
}
|
|
587
|
+
appendComments(table) {
|
|
588
|
+
const sql = [];
|
|
589
|
+
const schema = this.platform.quoteValue(table.schema);
|
|
590
|
+
const tableName = this.platform.quoteValue(table.name);
|
|
591
|
+
if (table.comment) {
|
|
592
|
+
const comment = this.platform.quoteValue(table.comment);
|
|
593
|
+
sql.push(`if exists(select * from sys.fn_listextendedproperty(N'MS_Description', N'Schema', N${schema}, N'Table', N${tableName}, null, null))
|
|
578
594
|
exec sys.sp_updateextendedproperty N'MS_Description', N${comment}, N'Schema', N${schema}, N'Table', N${tableName}
|
|
579
595
|
else
|
|
580
596
|
exec sys.sp_addextendedproperty N'MS_Description', N${comment}, N'Schema', N${schema}, N'Table', N${tableName}`);
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
597
|
+
}
|
|
598
|
+
for (const column of table.getColumns()) {
|
|
599
|
+
if (column.comment) {
|
|
600
|
+
const comment = this.platform.quoteValue(column.comment);
|
|
601
|
+
const columnName = this.platform.quoteValue(column.name);
|
|
602
|
+
sql.push(`if exists(select * from sys.fn_listextendedproperty(N'MS_Description', N'Schema', N${schema}, N'Table', N${tableName}, N'Column', N${columnName}))
|
|
587
603
|
exec sys.sp_updateextendedproperty N'MS_Description', N${comment}, N'Schema', N${schema}, N'Table', N${tableName}, N'Column', N${columnName}
|
|
588
604
|
else
|
|
589
605
|
exec sys.sp_addextendedproperty N'MS_Description', N${comment}, N'Schema', N${schema}, N'Table', N${tableName}, N'Column', N${columnName}`);
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
return sql;
|
|
609
|
+
}
|
|
610
|
+
inferLengthFromColumnType(type) {
|
|
611
|
+
const match = /^(\w+)\s*\(\s*(-?\d+|max)\s*\)/.exec(type);
|
|
612
|
+
if (!match) {
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
if (match[2] === 'max') {
|
|
616
|
+
return -1;
|
|
617
|
+
}
|
|
618
|
+
return +match[2];
|
|
619
|
+
}
|
|
620
|
+
wrap(val, type) {
|
|
621
|
+
const stringType =
|
|
622
|
+
type instanceof StringType ||
|
|
623
|
+
type instanceof TextType ||
|
|
624
|
+
type instanceof EnumType ||
|
|
625
|
+
type instanceof UnicodeStringType;
|
|
626
|
+
return typeof val === 'string' && val.length > 0 && stringType ? this.platform.quoteValue(val) : val;
|
|
627
|
+
}
|
|
611
628
|
}
|