@mikro-orm/mssql 7.1.0-dev.3 → 7.1.0-dev.30
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/MsSqlMikroORM.d.ts +3 -3
- package/MsSqlMikroORM.js +3 -2
- package/MsSqlPlatform.d.ts +1 -0
- package/MsSqlPlatform.js +9 -4
- package/MsSqlSchemaHelper.d.ts +10 -1
- package/MsSqlSchemaHelper.js +156 -13
- package/package.json +5 -5
package/MsSqlMikroORM.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type AnyEntity, type EntityClass, type EntitySchema, MikroORM, type Options, type IDatabaseDriver, type EntityManager, type EntityManagerType } from '@mikro-orm/core';
|
|
2
|
-
import type
|
|
1
|
+
import { type AnyEntity, type EntityClass, type EntitySchema, type MikroORM, type Options, type IDatabaseDriver, type EntityManager, type EntityManagerType } from '@mikro-orm/core';
|
|
2
|
+
import { SqlMikroORM, type SqlEntityManager } from '@mikro-orm/sql';
|
|
3
3
|
import { MsSqlDriver } from './MsSqlDriver.js';
|
|
4
4
|
/** Configuration options for the MSSQL driver. */
|
|
5
5
|
export type MsSqlOptions<EM extends SqlEntityManager<MsSqlDriver> = SqlEntityManager<MsSqlDriver>, Entities extends readonly (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]> = Partial<Options<MsSqlDriver, EM, Entities>>;
|
|
@@ -8,7 +8,7 @@ export declare function defineMsSqlConfig<EM extends SqlEntityManager<MsSqlDrive
|
|
|
8
8
|
/**
|
|
9
9
|
* @inheritDoc
|
|
10
10
|
*/
|
|
11
|
-
export declare class MsSqlMikroORM<EM extends SqlEntityManager<MsSqlDriver> = SqlEntityManager<MsSqlDriver>, Entities extends readonly (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]> extends
|
|
11
|
+
export declare class MsSqlMikroORM<EM extends SqlEntityManager<MsSqlDriver> = SqlEntityManager<MsSqlDriver>, Entities extends readonly (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]> extends SqlMikroORM<MsSqlDriver, EM, Entities> {
|
|
12
12
|
/**
|
|
13
13
|
* @inheritDoc
|
|
14
14
|
*/
|
package/MsSqlMikroORM.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { defineConfig,
|
|
1
|
+
import { defineConfig, } from '@mikro-orm/core';
|
|
2
|
+
import { SqlMikroORM } from '@mikro-orm/sql';
|
|
2
3
|
import { MsSqlDriver } from './MsSqlDriver.js';
|
|
3
4
|
/** Creates a type-safe configuration object for the MSSQL driver. */
|
|
4
5
|
export function defineMsSqlConfig(options) {
|
|
@@ -7,7 +8,7 @@ export function defineMsSqlConfig(options) {
|
|
|
7
8
|
/**
|
|
8
9
|
* @inheritDoc
|
|
9
10
|
*/
|
|
10
|
-
export class MsSqlMikroORM extends
|
|
11
|
+
export class MsSqlMikroORM extends SqlMikroORM {
|
|
11
12
|
/**
|
|
12
13
|
* @inheritDoc
|
|
13
14
|
*/
|
package/MsSqlPlatform.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export declare class MsSqlPlatform extends AbstractSqlPlatform {
|
|
|
8
8
|
#private;
|
|
9
9
|
protected readonly schemaHelper: MsSqlSchemaHelper;
|
|
10
10
|
protected readonly exceptionConverter: MsSqlExceptionConverter;
|
|
11
|
+
formatIndexHint(indexNames: string[]): string;
|
|
11
12
|
/** @inheritDoc */
|
|
12
13
|
lookupExtensions(orm: MikroORM<MsSqlDriver>): void;
|
|
13
14
|
/** @inheritDoc */
|
package/MsSqlPlatform.js
CHANGED
|
@@ -16,6 +16,9 @@ export class MsSqlPlatform extends AbstractSqlPlatform {
|
|
|
16
16
|
bigint: 'bigint',
|
|
17
17
|
boolean: 'bit',
|
|
18
18
|
};
|
|
19
|
+
formatIndexHint(indexNames) {
|
|
20
|
+
return `with (index(${indexNames.join(', ')}))`;
|
|
21
|
+
}
|
|
19
22
|
/** @inheritDoc */
|
|
20
23
|
lookupExtensions(orm) {
|
|
21
24
|
MsSqlSchemaGenerator.register(orm);
|
|
@@ -136,6 +139,7 @@ export class MsSqlPlatform extends AbstractSqlPlatform {
|
|
|
136
139
|
return 'uniqueidentifier';
|
|
137
140
|
}
|
|
138
141
|
validateMetadata(meta) {
|
|
142
|
+
super.validateMetadata(meta);
|
|
139
143
|
for (const prop of meta.props) {
|
|
140
144
|
if ((prop.runtimeType === 'string' || ['string', 'nvarchar'].includes(prop.type)) &&
|
|
141
145
|
!['uuid'].includes(prop.type) &&
|
|
@@ -150,16 +154,17 @@ export class MsSqlPlatform extends AbstractSqlPlatform {
|
|
|
150
154
|
getSearchJsonPropertyKey(path, type, aliased, value) {
|
|
151
155
|
const [a, ...b] = path;
|
|
152
156
|
/* v8 ignore next */
|
|
153
|
-
const root =
|
|
157
|
+
const root = aliased ? `[${ALIAS_REPLACEMENT}].${this.quoteIdentifier(a)}` : this.quoteIdentifier(a);
|
|
154
158
|
const types = {
|
|
155
159
|
boolean: 'bit',
|
|
156
160
|
};
|
|
157
161
|
const cast = (key) => raw(type in types ? `cast(${key} as ${types[type]})` : key);
|
|
162
|
+
const jsonPath = this.quoteValue(`$.${b.map(this.quoteJsonKey).join('.')}`);
|
|
158
163
|
/* v8 ignore next */
|
|
159
164
|
if (path.length === 0) {
|
|
160
|
-
return cast(`json_value(${root},
|
|
165
|
+
return cast(`json_value(${root}, ${jsonPath})`);
|
|
161
166
|
}
|
|
162
|
-
return cast(`json_value(${root},
|
|
167
|
+
return cast(`json_value(${root}, ${jsonPath})`);
|
|
163
168
|
}
|
|
164
169
|
getJsonArrayFromSQL(column, alias, properties) {
|
|
165
170
|
const columns = properties
|
|
@@ -187,7 +192,7 @@ export class MsSqlPlatform extends AbstractSqlPlatform {
|
|
|
187
192
|
if (RawQueryFragment.isKnownFragment(id)) {
|
|
188
193
|
return super.quoteIdentifier(id);
|
|
189
194
|
}
|
|
190
|
-
return `[${id.toString().replace('.', `].[`)}]`;
|
|
195
|
+
return `[${id.toString().replaceAll(']', ']]').replace('.', `].[`)}]`;
|
|
191
196
|
}
|
|
192
197
|
escape(value) {
|
|
193
198
|
if (value instanceof UnicodeString) {
|
package/MsSqlSchemaHelper.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AbstractSqlConnection, type CheckDef, type Column, type DatabaseSchema, type DatabaseTable, type Dictionary, type ForeignKey, type IndexDef, SchemaHelper, type Table, type TableDifference, type Transaction, type Type } from '@mikro-orm/sql';
|
|
1
|
+
import { type AbstractSqlConnection, type CheckDef, type Column, type DatabaseSchema, type DatabaseTable, type Dictionary, type ForeignKey, type IndexDef, SchemaHelper, type Table, type TableDifference, type SqlTriggerDef, type Transaction, type Type } from '@mikro-orm/sql';
|
|
2
2
|
/** Schema introspection helper for Microsoft SQL Server. */
|
|
3
3
|
export declare class MsSqlSchemaHelper extends SchemaHelper {
|
|
4
4
|
static readonly DEFAULT_VALUES: {
|
|
@@ -6,6 +6,8 @@ export declare class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
6
6
|
false: string[];
|
|
7
7
|
'getdate()': string[];
|
|
8
8
|
};
|
|
9
|
+
private static readonly AUTO_NOT_NULL_RE;
|
|
10
|
+
protected get bracketQuotedIdentifiers(): boolean;
|
|
9
11
|
getManagementDbName(): string;
|
|
10
12
|
getDropDatabaseSQL(name: string): string;
|
|
11
13
|
disableForeignKeysSQL(): string;
|
|
@@ -23,6 +25,13 @@ export declare class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
23
25
|
private getEnumDefinitions;
|
|
24
26
|
private getChecksSQL;
|
|
25
27
|
getAllChecks(connection: AbstractSqlConnection, tablesBySchemas: Map<string | undefined, Table[]>, ctx?: Transaction): Promise<Dictionary<CheckDef[]>>;
|
|
28
|
+
/** Generates SQL to create an MSSQL trigger. MSSQL supports AFTER and INSTEAD OF only. */
|
|
29
|
+
createTrigger(table: DatabaseTable, trigger: SqlTriggerDef): string;
|
|
30
|
+
/** Generates SQL to drop an MSSQL trigger. */
|
|
31
|
+
dropTrigger(table: DatabaseTable, trigger: SqlTriggerDef): string;
|
|
32
|
+
private getSchemaQualifiedName;
|
|
33
|
+
getDatabaseCollation(connection: AbstractSqlConnection, ctx?: Transaction): Promise<string | undefined>;
|
|
34
|
+
getAllTriggers(connection: AbstractSqlConnection, tablesBySchemas: Map<string | undefined, Table[]>): Promise<Dictionary<SqlTriggerDef[]>>;
|
|
26
35
|
loadInformationSchema(schema: DatabaseSchema, connection: AbstractSqlConnection, tables: Table[], schemas?: string[], ctx?: Transaction): Promise<void>;
|
|
27
36
|
getPreAlterTable(tableDiff: TableDifference, safe: boolean): string[];
|
|
28
37
|
getPostAlterTable(tableDiff: TableDifference, safe: boolean): string[];
|
package/MsSqlSchemaHelper.js
CHANGED
|
@@ -7,12 +7,43 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
7
7
|
false: ['0'],
|
|
8
8
|
'getdate()': ['current_timestamp'],
|
|
9
9
|
};
|
|
10
|
+
// `stripAutoNotNullFilter` unwraps balanced per-clause parens before calling `.exec`, so we
|
|
11
|
+
// only need to match the bare form here — previously the pattern allowed independently
|
|
12
|
+
// optional leading/trailing parens, which accepted unbalanced strings like `([col] IS NOT NULL`.
|
|
13
|
+
static AUTO_NOT_NULL_RE = /^\[([^\]]+)\]\s+IS\s+NOT\s+NULL$/i;
|
|
14
|
+
// MSSQL `filter_definition` and `where` predicates use `[…]` bracket-quoting for identifiers,
|
|
15
|
+
// so `splitTopLevelAnd` must treat `[` as opening a quoted span (otherwise `[some and col]`
|
|
16
|
+
// would split mid-identifier).
|
|
17
|
+
get bracketQuotedIdentifiers() {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
10
20
|
getManagementDbName() {
|
|
11
21
|
return 'master';
|
|
12
22
|
}
|
|
13
23
|
getDropDatabaseSQL(name) {
|
|
24
|
+
// `set offline` rejects all connections including the issuing session, so there is no
|
|
25
|
+
// single-user race window where a torn-down pool connection can reconnect between the
|
|
26
|
+
// mode switch and the drop (SQL Server error 3702 "currently in use"). Dropping an
|
|
27
|
+
// offline database leaves the underlying `.mdf`/`.ldf` files behind though, which
|
|
28
|
+
// makes a subsequent `create database` with the same name fail with error 5170
|
|
29
|
+
// ("file already exists"). Capture the physical paths up front and call
|
|
30
|
+
// `master.sys.xp_delete_files` after the drop to clean them up.
|
|
14
31
|
const quoted = this.quote(name);
|
|
15
|
-
|
|
32
|
+
const literal = this.platform.quoteValue(name);
|
|
33
|
+
return (`if db_id(${literal}) is not null begin ` +
|
|
34
|
+
`declare @drop_files table (path nvarchar(260)); ` +
|
|
35
|
+
`insert into @drop_files (path) select physical_name from sys.master_files where database_id = db_id(${literal}); ` +
|
|
36
|
+
`alter database ${quoted} set offline with rollback immediate; ` +
|
|
37
|
+
`drop database ${quoted}; ` +
|
|
38
|
+
`declare @drop_path nvarchar(260); ` +
|
|
39
|
+
`declare drop_files_cursor cursor local fast_forward for select path from @drop_files; ` +
|
|
40
|
+
`open drop_files_cursor; fetch next from drop_files_cursor into @drop_path; ` +
|
|
41
|
+
`while @@fetch_status = 0 begin ` +
|
|
42
|
+
`begin try exec master.sys.xp_delete_files @drop_path; end try begin catch end catch; ` +
|
|
43
|
+
`fetch next from drop_files_cursor into @drop_path; ` +
|
|
44
|
+
`end ` +
|
|
45
|
+
`close drop_files_cursor; deallocate drop_files_cursor; ` +
|
|
46
|
+
`end`);
|
|
16
47
|
}
|
|
17
48
|
disableForeignKeysSQL() {
|
|
18
49
|
return `exec sp_MSforeachtable 'alter table ? nocheck constraint all';`;
|
|
@@ -83,7 +114,8 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
83
114
|
numeric_scale as numeric_scale,
|
|
84
115
|
datetime_precision as datetime_precision,
|
|
85
116
|
character_maximum_length as character_maximum_length,
|
|
86
|
-
columnproperty(sc.object_id, column_name, 'IsIdentity') is_identity
|
|
117
|
+
columnproperty(sc.object_id, column_name, 'IsIdentity') is_identity,
|
|
118
|
+
nullif(ic.collation_name, convert(nvarchar(128), databasepropertyex(db_name(), 'Collation'))) as collation_name
|
|
87
119
|
from information_schema.columns ic
|
|
88
120
|
inner join sys.columns sc on sc.name = ic.column_name and sc.object_id = object_id(ic.table_schema + '.' + ic.table_name)
|
|
89
121
|
left join sys.computed_columns cmp on cmp.name = ic.column_name and cmp.object_id = object_id(ic.table_schema + '.' + ic.table_name)
|
|
@@ -135,6 +167,7 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
135
167
|
precision: col.numeric_precision,
|
|
136
168
|
scale: col.numeric_scale,
|
|
137
169
|
comment: col.column_comment,
|
|
170
|
+
collation: col.collation_name ?? undefined,
|
|
138
171
|
generated,
|
|
139
172
|
});
|
|
140
173
|
}
|
|
@@ -148,6 +181,7 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
148
181
|
col.name as column_name,
|
|
149
182
|
schema_name(t.schema_id) as schema_name,
|
|
150
183
|
(case when filter_definition is not null then concat('where ', filter_definition) else null end) as expression,
|
|
184
|
+
filter_definition as filter_definition,
|
|
151
185
|
ind.is_disabled as is_disabled,
|
|
152
186
|
ind.type as index_type,
|
|
153
187
|
ind.fill_factor as fill_factor,
|
|
@@ -192,15 +226,31 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
192
226
|
if (index.fill_factor > 0) {
|
|
193
227
|
indexDef.fillFactor = index.fill_factor;
|
|
194
228
|
}
|
|
195
|
-
|
|
196
|
-
|
|
229
|
+
/* v8 ignore next: function-based / computed-column introspection path, same as pre-PR */
|
|
230
|
+
if (index.column_name?.match(/[(): ,"'`]/)) {
|
|
231
|
+
indexDef.expression = index.expression;
|
|
197
232
|
indexDef.expression = this.getCreateIndexSQL(index.table_name, indexDef, !!index.expression);
|
|
198
233
|
}
|
|
234
|
+
else if (index.filter_definition) {
|
|
235
|
+
// Auto-NOT-NULL stripping runs post-mapIndexes (needs the consolidated column list).
|
|
236
|
+
indexDef.where = index.filter_definition;
|
|
237
|
+
}
|
|
199
238
|
ret[key] ??= [];
|
|
200
239
|
ret[key].push(indexDef);
|
|
201
240
|
}
|
|
202
241
|
for (const key of Object.keys(ret)) {
|
|
203
242
|
ret[key] = await this.mapIndexes(ret[key]);
|
|
243
|
+
for (const idx of ret[key]) {
|
|
244
|
+
if (idx.where) {
|
|
245
|
+
const stripped = this.stripAutoNotNullFilter(idx.where, idx.columnNames, MsSqlSchemaHelper.AUTO_NOT_NULL_RE);
|
|
246
|
+
if (stripped === '') {
|
|
247
|
+
delete idx.where;
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
idx.where = stripped;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
204
254
|
}
|
|
205
255
|
return ret;
|
|
206
256
|
}
|
|
@@ -285,6 +335,86 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
285
335
|
}
|
|
286
336
|
return ret;
|
|
287
337
|
}
|
|
338
|
+
/** Generates SQL to create an MSSQL trigger. MSSQL supports AFTER and INSTEAD OF only. */
|
|
339
|
+
createTrigger(table, trigger) {
|
|
340
|
+
if (trigger.expression) {
|
|
341
|
+
return trigger.expression;
|
|
342
|
+
}
|
|
343
|
+
/* v8 ignore next 3 */
|
|
344
|
+
if (trigger.timing === 'before') {
|
|
345
|
+
throw new Error(`MSSQL does not support BEFORE triggers. Use AFTER or INSTEAD OF for trigger "${trigger.name}".`);
|
|
346
|
+
}
|
|
347
|
+
const timing = trigger.timing.toUpperCase();
|
|
348
|
+
const events = trigger.events.map(e => e.toUpperCase()).join(', ');
|
|
349
|
+
const qualifiedName = this.getSchemaQualifiedName(table, trigger.name);
|
|
350
|
+
return `create trigger ${qualifiedName} on ${table.getQuotedName()} ${timing} ${events} as begin ${trigger.body}; end`;
|
|
351
|
+
}
|
|
352
|
+
/** Generates SQL to drop an MSSQL trigger. */
|
|
353
|
+
dropTrigger(table, trigger) {
|
|
354
|
+
return `drop trigger if exists ${this.getSchemaQualifiedName(table, trigger.name)}`;
|
|
355
|
+
}
|
|
356
|
+
getSchemaQualifiedName(table, name) {
|
|
357
|
+
const defaultSchema = this.platform.getDefaultSchemaName();
|
|
358
|
+
if (table.schema && table.schema !== defaultSchema) {
|
|
359
|
+
return `${this.quote(table.schema)}.${this.quote(name)}`;
|
|
360
|
+
}
|
|
361
|
+
return this.quote(name);
|
|
362
|
+
}
|
|
363
|
+
async getDatabaseCollation(connection, ctx) {
|
|
364
|
+
const [row] = await connection.execute(`select convert(nvarchar(128), databasepropertyex(db_name(), 'Collation')) as collation`, [], 'all', ctx);
|
|
365
|
+
return row?.collation;
|
|
366
|
+
}
|
|
367
|
+
async getAllTriggers(connection, tablesBySchemas) {
|
|
368
|
+
const conditions = [];
|
|
369
|
+
for (const [schema, tables] of tablesBySchemas) {
|
|
370
|
+
const names = tables.map(t => this.platform.quoteValue(t.table_name)).join(', ');
|
|
371
|
+
const schemaName = this.platform.quoteValue(schema ?? this.platform.getDefaultSchemaName());
|
|
372
|
+
conditions.push(`(schema_name(p.schema_id) = ${schemaName} and p.name in (${names}))`);
|
|
373
|
+
}
|
|
374
|
+
const sql = `select t.name as trigger_name, schema_name(p.schema_id) as schema_name,
|
|
375
|
+
p.name as table_name, te.type_desc as event,
|
|
376
|
+
case when t.is_instead_of_trigger = 1 then 'INSTEAD OF' else 'AFTER' end as timing,
|
|
377
|
+
object_definition(t.object_id) as definition
|
|
378
|
+
from sys.triggers t
|
|
379
|
+
join sys.trigger_events te on t.object_id = te.object_id
|
|
380
|
+
join sys.objects p on t.parent_id = p.object_id
|
|
381
|
+
where (${conditions.join(' or ')})
|
|
382
|
+
order by t.name, te.type_desc`;
|
|
383
|
+
const allTriggers = await connection.execute(sql);
|
|
384
|
+
const ret = {};
|
|
385
|
+
const triggerMap = new Map();
|
|
386
|
+
for (const row of allTriggers) {
|
|
387
|
+
const key = this.getTableKey(row);
|
|
388
|
+
const dedupeKey = `${key}:${row.trigger_name}`;
|
|
389
|
+
const event = row.event.toLowerCase();
|
|
390
|
+
if (triggerMap.has(dedupeKey)) {
|
|
391
|
+
const existing = triggerMap.get(dedupeKey);
|
|
392
|
+
if (!existing.events.includes(event)) {
|
|
393
|
+
existing.events.push(event);
|
|
394
|
+
}
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
// Parse body from full trigger definition
|
|
398
|
+
let body = '';
|
|
399
|
+
if (row.definition) {
|
|
400
|
+
const bodyMatch = /\bas\s+begin\s+([\s\S]*)\s*end\s*;?\s*$/i.exec(row.definition);
|
|
401
|
+
if (bodyMatch) {
|
|
402
|
+
body = bodyMatch[1].trim().replace(/;\s*$/, '');
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
ret[key] ??= [];
|
|
406
|
+
const trigger = {
|
|
407
|
+
name: row.trigger_name,
|
|
408
|
+
timing: row.timing.toLowerCase(),
|
|
409
|
+
events: [event],
|
|
410
|
+
forEach: 'row', // MSSQL has no FOR EACH ROW/STATEMENT syntax; match the metadata default to avoid false diffs
|
|
411
|
+
body,
|
|
412
|
+
};
|
|
413
|
+
ret[key].push(trigger);
|
|
414
|
+
triggerMap.set(dedupeKey, trigger);
|
|
415
|
+
}
|
|
416
|
+
return ret;
|
|
417
|
+
}
|
|
288
418
|
async loadInformationSchema(schema, connection, tables, schemas, ctx) {
|
|
289
419
|
if (tables.length === 0) {
|
|
290
420
|
return;
|
|
@@ -294,12 +424,18 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
294
424
|
const indexes = await this.getAllIndexes(connection, tablesBySchema, ctx);
|
|
295
425
|
const checks = await this.getAllChecks(connection, tablesBySchema, ctx);
|
|
296
426
|
const fks = await this.getAllForeignKeys(connection, tablesBySchema, ctx);
|
|
427
|
+
const triggers = await this.getAllTriggers(connection, tablesBySchema);
|
|
428
|
+
const dbCollation = await this.getDatabaseCollation(connection, ctx);
|
|
297
429
|
for (const t of tables) {
|
|
298
430
|
const key = this.getTableKey(t);
|
|
299
431
|
const table = schema.addTable(t.table_name, t.schema_name, t.table_comment);
|
|
432
|
+
table.collation = dbCollation;
|
|
300
433
|
const pks = await this.getPrimaryKeys(connection, indexes[key], table.name, table.schema);
|
|
301
434
|
const enums = this.getEnumDefinitions(checks[key] ?? []);
|
|
302
435
|
table.init(columns[key], indexes[key], checks[key], pks, fks[key], enums);
|
|
436
|
+
if (triggers[key]) {
|
|
437
|
+
table.setTriggers(triggers[key]);
|
|
438
|
+
}
|
|
303
439
|
}
|
|
304
440
|
}
|
|
305
441
|
getPreAlterTable(tableDiff, safe) {
|
|
@@ -415,7 +551,11 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
415
551
|
else {
|
|
416
552
|
col.push(columnType);
|
|
417
553
|
}
|
|
418
|
-
Utils.runIfNotEmpty(() => col.push(
|
|
554
|
+
Utils.runIfNotEmpty(() => col.push(this.getCollateSQL(column.collation)), column.collation);
|
|
555
|
+
// `IDENTITY(1,1)` is rejected inside `ALTER COLUMN`, so it must only be emitted when the
|
|
556
|
+
// change actually involves the identity attribute or is a fresh column (no `changedProperties`).
|
|
557
|
+
Utils.runIfNotEmpty(() => col.push('identity(1,1)'), column.autoincrement &&
|
|
558
|
+
(!changedProperties || changedProperties.has('autoincrement') || changedProperties.has('type')));
|
|
419
559
|
Utils.runIfNotEmpty(() => col.push('null'), column.nullable);
|
|
420
560
|
Utils.runIfNotEmpty(() => col.push('not null'), !column.nullable && !column.generated);
|
|
421
561
|
if (column.autoincrement &&
|
|
@@ -437,7 +577,7 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
437
577
|
const [constraint] = this.getDropDefaultsSQL(table.name, [column], table.schema);
|
|
438
578
|
parts.push(constraint);
|
|
439
579
|
}
|
|
440
|
-
if (changedProperties.has('type') || changedProperties.has('nullable')) {
|
|
580
|
+
if (changedProperties.has('type') || changedProperties.has('nullable') || changedProperties.has('collation')) {
|
|
441
581
|
const col = this.createTableColumn(column, table, changedProperties);
|
|
442
582
|
parts.push(`alter table ${table.getQuotedName()} alter column ${col}`);
|
|
443
583
|
}
|
|
@@ -460,7 +600,7 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
460
600
|
const clustered = index.clustered ? 'clustered ' : '';
|
|
461
601
|
let sql = `create ${index.unique ? 'unique ' : ''}${clustered}index ${keyName} on ${this.quote(tableName)} `;
|
|
462
602
|
if (index.expression && partialExpression) {
|
|
463
|
-
return sql + `(${index.expression})` + this.getMsSqlIndexSuffix(index);
|
|
603
|
+
return sql + `(${index.expression})` + this.getMsSqlIndexSuffix(index) + this.getIndexWhereClause(index);
|
|
464
604
|
}
|
|
465
605
|
// Build column list with advanced options
|
|
466
606
|
const columns = this.getIndexColumns(index);
|
|
@@ -469,7 +609,7 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
469
609
|
if (index.include?.length) {
|
|
470
610
|
sql += ` include (${index.include.map(c => this.quote(c)).join(', ')})`;
|
|
471
611
|
}
|
|
472
|
-
sql += this.getMsSqlIndexSuffix(index);
|
|
612
|
+
sql += this.getMsSqlIndexSuffix(index) + this.getIndexWhereClause(index);
|
|
473
613
|
// Disabled indexes need to be created first, then disabled
|
|
474
614
|
if (index.disabled) {
|
|
475
615
|
sql += `;\nalter index ${keyName} on ${this.quote(tableName)} disable`;
|
|
@@ -514,13 +654,16 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
514
654
|
if (index.expression) {
|
|
515
655
|
return index.expression;
|
|
516
656
|
}
|
|
517
|
-
const
|
|
518
|
-
if (!
|
|
657
|
+
const needsAutoNotNull = index.unique && index.columnNames.some(column => table.getColumn(column)?.nullable);
|
|
658
|
+
if (!needsAutoNotNull) {
|
|
519
659
|
return this.getCreateIndexSQL(table.getShortestName(), index);
|
|
520
660
|
}
|
|
521
|
-
//
|
|
522
|
-
|
|
523
|
-
|
|
661
|
+
// Strip `index.where` from the base SQL so we can combine it with the auto NOT-NULL guard
|
|
662
|
+
// ourselves, wrapping the user predicate in parens to defuse operator precedence
|
|
663
|
+
// (a bare `a = 1 or b = 2 and [col] is not null` would bind as `a = 1 or (b = 2 and …)`).
|
|
664
|
+
let sql = this.getCreateIndexSQL(table.getShortestName(), { ...index, where: undefined, disabled: false });
|
|
665
|
+
const autoNotNull = index.columnNames.map(c => `${this.quote(c)} is not null`).join(' and ');
|
|
666
|
+
sql += index.where ? ` where (${index.where}) and ${autoNotNull}` : ` where ${autoNotNull}`;
|
|
524
667
|
if (index.disabled) {
|
|
525
668
|
sql += `;\nalter index ${this.quote(index.keyName)} on ${table.getQuotedName()} disable`;
|
|
526
669
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/mssql",
|
|
3
|
-
"version": "7.1.0-dev.
|
|
3
|
+
"version": "7.1.0-dev.30",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"data-mapper",
|
|
@@ -47,17 +47,17 @@
|
|
|
47
47
|
"copy": "node ../../scripts/copy.mjs"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@mikro-orm/sql": "7.1.0-dev.
|
|
51
|
-
"kysely": "0.28.
|
|
50
|
+
"@mikro-orm/sql": "7.1.0-dev.30",
|
|
51
|
+
"kysely": "0.28.17",
|
|
52
52
|
"tarn": "3.0.2",
|
|
53
53
|
"tedious": "19.2.1",
|
|
54
54
|
"tsqlstring": "1.0.1"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@mikro-orm/core": "^7.0.
|
|
57
|
+
"@mikro-orm/core": "^7.0.15"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
|
-
"@mikro-orm/core": "7.1.0-dev.
|
|
60
|
+
"@mikro-orm/core": "7.1.0-dev.30"
|
|
61
61
|
},
|
|
62
62
|
"engines": {
|
|
63
63
|
"node": ">= 22.17.0"
|