@mikro-orm/mssql 7.0.0-dev.33 → 7.0.0-dev.330

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.
@@ -1,4 +1,4 @@
1
- import { AbstractSqlConnection, type TransactionEventBroadcaster } from '@mikro-orm/knex';
1
+ import { AbstractSqlConnection, type TransactionEventBroadcaster } from '@mikro-orm/sql';
2
2
  import { type ControlledTransaction, MssqlDialect } from 'kysely';
3
3
  import type { ConnectionConfiguration } from 'tedious';
4
4
  export declare class MsSqlConnection extends AbstractSqlConnection {
@@ -1,4 +1,4 @@
1
- import { AbstractSqlConnection, Utils, } from '@mikro-orm/knex';
1
+ import { AbstractSqlConnection, Utils } from '@mikro-orm/sql';
2
2
  import { MssqlDialect } from 'kysely';
3
3
  import * as Tedious from 'tedious';
4
4
  import * as Tarn from 'tarn';
@@ -45,7 +45,7 @@ export class MsSqlConnection extends AbstractSqlConnection {
45
45
  },
46
46
  server: options.host,
47
47
  };
48
- /* v8 ignore next 6 */
48
+ /* v8 ignore next */
49
49
  if (ret.server.includes('\\')) {
50
50
  const [host, ...name] = ret.server.split('\\');
51
51
  ret.server = host;
@@ -68,7 +68,7 @@ export class MsSqlConnection extends AbstractSqlConnection {
68
68
  return res.rows;
69
69
  }
70
70
  const rowCount = res.rows.length;
71
- const hasEmptyCount = (rowCount === 1) && ('' in res.rows[0]);
71
+ const hasEmptyCount = rowCount === 1 && '' in res.rows[0];
72
72
  const emptyRow = hasEmptyCount && Number(res.rows[0]['']);
73
73
  return {
74
74
  affectedRows: hasEmptyCount ? emptyRow : Number(res.numAffectedRows),
package/MsSqlDriver.d.ts CHANGED
@@ -1,10 +1,13 @@
1
- import { type AnyEntity, type Configuration, type ConnectionType, type EntityDictionary, type LoggingOptions, type NativeInsertUpdateManyOptions, type QueryResult, type Transaction } from '@mikro-orm/core';
2
- import { AbstractSqlDriver, type SqlEntityManager } from '@mikro-orm/knex';
1
+ import { type AnyEntity, type Configuration, type ConnectionType, type EntityDictionary, type EntityName, type LoggingOptions, type NativeInsertUpdateManyOptions, type QueryResult, type Transaction, type Constructor } from '@mikro-orm/core';
2
+ import { AbstractSqlDriver, type SqlEntityManager } from '@mikro-orm/sql';
3
3
  import { MsSqlConnection } from './MsSqlConnection.js';
4
4
  import { MsSqlQueryBuilder } from './MsSqlQueryBuilder.js';
5
+ import { MsSqlMikroORM } from './MsSqlMikroORM.js';
5
6
  export declare class MsSqlDriver extends AbstractSqlDriver<MsSqlConnection> {
6
7
  constructor(config: Configuration);
7
- nativeInsertMany<T extends AnyEntity<T>>(entityName: string, data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T>): Promise<QueryResult<T>>;
8
- createQueryBuilder<T extends AnyEntity<T>>(entityName: string, ctx?: Transaction, preferredConnectionType?: ConnectionType, convertCustomTypes?: boolean, loggerContext?: LoggingOptions, alias?: string, em?: SqlEntityManager): MsSqlQueryBuilder<T, any, any, any>;
8
+ nativeInsertMany<T extends AnyEntity<T>>(entityName: EntityName<T>, data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T>): Promise<QueryResult<T>>;
9
+ createQueryBuilder<T extends AnyEntity<T>>(entityName: EntityName<T>, ctx?: Transaction, preferredConnectionType?: ConnectionType, convertCustomTypes?: boolean, loggerContext?: LoggingOptions, alias?: string, em?: SqlEntityManager): MsSqlQueryBuilder<T, any, any, any>;
9
10
  private appendOutputTable;
11
+ /** @inheritDoc */
12
+ getORMClass(): Constructor<MsSqlMikroORM>;
10
13
  }
package/MsSqlDriver.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { QueryFlag, Utils, isRaw, } from '@mikro-orm/core';
2
- import { AbstractSqlDriver } from '@mikro-orm/knex';
2
+ import { AbstractSqlDriver } from '@mikro-orm/sql';
3
3
  import { MsSqlConnection } from './MsSqlConnection.js';
4
4
  import { MsSqlPlatform } from './MsSqlPlatform.js';
5
5
  import { MsSqlQueryBuilder } from './MsSqlQueryBuilder.js';
6
+ import { MsSqlMikroORM } from './MsSqlMikroORM.js';
6
7
  export class MsSqlDriver extends AbstractSqlDriver {
7
8
  constructor(config) {
8
9
  super(config, new MsSqlPlatform(), MsSqlConnection, ['kysely', 'tedious']);
@@ -21,10 +22,12 @@ export class MsSqlDriver extends AbstractSqlDriver {
21
22
  const returningFields = Utils.flatten(returningProps.map(prop => prop.fieldNames));
22
23
  const using2 = `select * from (values ${data.map((x, i) => `(${i})`).join(',')}) v (id) where 1 = 1`;
23
24
  /* v8 ignore next */
24
- const output = returningFields.length > 0 ? `output ${returningFields.map(field => 'inserted.' + this.platform.quoteIdentifier(field)).join(', ')}` : '';
25
+ const output = returningFields.length > 0
26
+ ? `output ${returningFields.map(field => 'inserted.' + this.platform.quoteIdentifier(field)).join(', ')}`
27
+ : '';
25
28
  const sql = `merge into ${tableName} using (${using2}) s on 1 = 0 when not matched then insert default values ${output};`;
26
29
  const res = await this.execute(sql, [], 'run', options.ctx);
27
- const pks = this.getPrimaryKeyFields(entityName);
30
+ const pks = this.getPrimaryKeyFields(meta);
28
31
  if (pks.length === 1) {
29
32
  res.row ??= {};
30
33
  res.rows ??= [];
@@ -32,7 +35,8 @@ export class MsSqlDriver extends AbstractSqlDriver {
32
35
  }
33
36
  return res;
34
37
  }
35
- if (props.some(prop => prop.autoincrement)) {
38
+ // For TPT child entities, the parent table owns the identity column, not the child table
39
+ if (props.some(prop => prop.autoincrement && (!meta.ownProps || meta.ownProps.includes(prop)))) {
36
40
  return super.nativeInsertMany(entityName, data, options, sql => {
37
41
  return `set identity_insert ${tableName} on; ${sql}; set identity_insert ${tableName} off`;
38
42
  });
@@ -41,7 +45,9 @@ export class MsSqlDriver extends AbstractSqlDriver {
41
45
  }
42
46
  createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes, loggerContext, alias, em) {
43
47
  // do not compute the connectionType if EM is provided as it will be computed from it in the QB later on
44
- const connectionType = em ? preferredConnectionType : this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
48
+ const connectionType = em
49
+ ? preferredConnectionType
50
+ : this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
45
51
  const qb = new MsSqlQueryBuilder(entityName, this.metadata, this, ctx, alias, connectionType, em, loggerContext);
46
52
  if (!convertCustomTypes) {
47
53
  qb.unsetFlag(QueryFlag.CONVERT_CUSTOM_TYPES);
@@ -51,17 +57,15 @@ export class MsSqlDriver extends AbstractSqlDriver {
51
57
  appendOutputTable(entityName, data, sql) {
52
58
  const meta = this.metadata.get(entityName);
53
59
  const returningProps = meta.props
54
- .filter(prop => prop.persist !== false && prop.defaultRaw || prop.autoincrement || prop.generated)
60
+ .filter(prop => (prop.persist !== false && prop.defaultRaw) || prop.autoincrement || prop.generated)
55
61
  .filter(prop => !(prop.name in data[0]) || isRaw(data[0][prop.name]));
56
62
  const returningFields = Utils.flatten(returningProps.map(prop => prop.fieldNames));
57
- /* v8 ignore next 3 */
63
+ /* v8 ignore next */
58
64
  if (returningFields.length === 0) {
59
65
  return sql;
60
66
  }
61
67
  const tableName = this.getTableName(meta, {}, true);
62
- const selections = returningFields
63
- .map((field) => `[t].${this.platform.quoteIdentifier(field)}`)
64
- .join(',');
68
+ const selections = returningFields.map((field) => `[t].${this.platform.quoteIdentifier(field)}`).join(',');
65
69
  const position = sql.indexOf(' values ');
66
70
  const sqlBeforeValues = sql.substring(0, position);
67
71
  const sqlAfterValues = sql.substring(position + 1);
@@ -71,4 +75,8 @@ export class MsSqlDriver extends AbstractSqlDriver {
71
75
  outputSql += `drop table #out`;
72
76
  return outputSql;
73
77
  }
78
+ /** @inheritDoc */
79
+ getORMClass() {
80
+ return MsSqlMikroORM;
81
+ }
74
82
  }
@@ -1,8 +1,8 @@
1
1
  import { ExceptionConverter, type Dictionary, type DriverException } from '@mikro-orm/core';
2
2
  export declare class MsSqlExceptionConverter extends ExceptionConverter {
3
3
  /**
4
- * @link https://docs.microsoft.com/en-us/sql/relational-databases/errors-events/mssqlserver-511-database-engine-error?view=sql-server-ver15
5
- * @link https://github.com/doctrine/dbal/blob/master/src/Driver/AbstractPostgreSQLDriver.php
4
+ * @see https://docs.microsoft.com/en-us/sql/relational-databases/errors-events/mssqlserver-511-database-engine-error?view=sql-server-ver15
5
+ * @see https://github.com/doctrine/dbal/blob/master/src/Driver/AbstractPostgreSQLDriver.php
6
6
  */
7
7
  convertException(exception: Error & Dictionary): DriverException;
8
8
  }
@@ -1,13 +1,16 @@
1
1
  import { ExceptionConverter, InvalidFieldNameException, NonUniqueFieldNameException, NotNullConstraintViolationException, SyntaxErrorException, TableExistsException, TableNotFoundException, UniqueConstraintViolationException, } from '@mikro-orm/core';
2
2
  export class MsSqlExceptionConverter extends ExceptionConverter {
3
3
  /**
4
- * @link https://docs.microsoft.com/en-us/sql/relational-databases/errors-events/mssqlserver-511-database-engine-error?view=sql-server-ver15
5
- * @link https://github.com/doctrine/dbal/blob/master/src/Driver/AbstractPostgreSQLDriver.php
4
+ * @see https://docs.microsoft.com/en-us/sql/relational-databases/errors-events/mssqlserver-511-database-engine-error?view=sql-server-ver15
5
+ * @see https://github.com/doctrine/dbal/blob/master/src/Driver/AbstractPostgreSQLDriver.php
6
6
  */
7
7
  convertException(exception) {
8
8
  let errno = exception.number;
9
- /* v8 ignore next 5 */
10
- if ('errors' in exception && Array.isArray(exception.errors) && typeof exception.errors[0] === 'object' && 'message' in exception.errors[0]) {
9
+ /* v8 ignore next */
10
+ if ('errors' in exception &&
11
+ Array.isArray(exception.errors) &&
12
+ typeof exception.errors[0] === 'object' &&
13
+ 'message' in exception.errors[0]) {
11
14
  exception.message += '\n' + exception.errors.map(e => e.message).join('\n');
12
15
  errno ??= exception.errors[0].number;
13
16
  exception.lineNumber ??= exception.errors[0].lineNumber;
@@ -1,19 +1,18 @@
1
- import { MikroORM, type Options, type IDatabaseDriver, type EntityManager, type EntityManagerType } from '@mikro-orm/core';
1
+ import { type AnyEntity, type EntityClass, type EntitySchema, MikroORM, type Options, type IDatabaseDriver, type EntityManager, type EntityManagerType } from '@mikro-orm/core';
2
+ import type { SqlEntityManager } from '@mikro-orm/sql';
2
3
  import { MsSqlDriver } from './MsSqlDriver.js';
3
- import type { SqlEntityManager } from '@mikro-orm/knex';
4
+ export type MsSqlOptions<EM extends SqlEntityManager<MsSqlDriver> = SqlEntityManager<MsSqlDriver>, Entities extends (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]> = Partial<Options<MsSqlDriver, EM, Entities>>;
5
+ export declare function defineMsSqlConfig<EM extends SqlEntityManager<MsSqlDriver> = SqlEntityManager<MsSqlDriver>, Entities extends (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]>(options: Partial<Options<MsSqlDriver, EM, Entities>>): Partial<Options<MsSqlDriver, EM, Entities>>;
4
6
  /**
5
7
  * @inheritDoc
6
8
  */
7
- export declare class MsSqlMikroORM<EM extends EntityManager = SqlEntityManager> extends MikroORM<MsSqlDriver, EM> {
8
- private static DRIVER;
9
+ export declare class MsSqlMikroORM<EM extends SqlEntityManager<MsSqlDriver> = SqlEntityManager<MsSqlDriver>, Entities extends (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]> extends MikroORM<MsSqlDriver, EM, Entities> {
9
10
  /**
10
11
  * @inheritDoc
11
12
  */
12
- static init<D extends IDatabaseDriver = MsSqlDriver, EM extends EntityManager = D[typeof EntityManagerType] & EntityManager>(options?: Options<D, EM>): Promise<MikroORM<D, EM>>;
13
+ static init<D extends IDatabaseDriver = MsSqlDriver, EM extends EntityManager<D> = D[typeof EntityManagerType] & EntityManager<D>, Entities extends (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]>(options: Partial<Options<D, EM, Entities>>): Promise<MikroORM<D, EM, Entities>>;
13
14
  /**
14
15
  * @inheritDoc
15
16
  */
16
- static initSync<D extends IDatabaseDriver = MsSqlDriver, EM extends EntityManager = D[typeof EntityManagerType] & EntityManager>(options: Options<D, EM>): MikroORM<D, EM>;
17
+ constructor(options: Partial<Options<MsSqlDriver, EM, Entities>>);
17
18
  }
18
- export type MsSqlOptions = Options<MsSqlDriver>;
19
- export declare function defineMsSqlConfig(options: MsSqlOptions): Options<MsSqlDriver, SqlEntityManager<MsSqlDriver> & EntityManager<IDatabaseDriver<import("@mikro-orm/core").Connection>>>;
package/MsSqlMikroORM.js CHANGED
@@ -1,24 +1,22 @@
1
1
  import { defineConfig, MikroORM, } from '@mikro-orm/core';
2
2
  import { MsSqlDriver } from './MsSqlDriver.js';
3
+ export function defineMsSqlConfig(options) {
4
+ return defineConfig({ driver: MsSqlDriver, ...options });
5
+ }
3
6
  /**
4
7
  * @inheritDoc
5
8
  */
6
9
  export class MsSqlMikroORM extends MikroORM {
7
- static DRIVER = MsSqlDriver;
8
10
  /**
9
11
  * @inheritDoc
10
12
  */
11
13
  static async init(options) {
12
- return super.init(options);
14
+ return super.init(defineMsSqlConfig(options));
13
15
  }
14
16
  /**
15
17
  * @inheritDoc
16
18
  */
17
- static initSync(options) {
18
- return super.initSync(options);
19
+ constructor(options) {
20
+ super(defineMsSqlConfig(options));
19
21
  }
20
22
  }
21
- /* v8 ignore next 3 */
22
- export function defineMsSqlConfig(options) {
23
- return defineConfig({ driver: MsSqlDriver, ...options });
24
- }
@@ -1,12 +1,13 @@
1
- import { AbstractSqlPlatform, type EntityMetadata, type IDatabaseDriver, type EntityManager, type MikroORM, Type, type Primary, type IPrimaryKey, QueryOrder, MsSqlNativeQueryBuilder } from '@mikro-orm/knex';
1
+ import { AbstractSqlPlatform, type EntityManager, type EntityMetadata, type IDatabaseDriver, type IPrimaryKey, type MikroORM, MsSqlNativeQueryBuilder, type Primary, QueryOrder, RawQueryFragment, Type } from '@mikro-orm/sql';
2
2
  import { MsSqlSchemaHelper } from './MsSqlSchemaHelper.js';
3
3
  import { MsSqlExceptionConverter } from './MsSqlExceptionConverter.js';
4
4
  import { MsSqlSchemaGenerator } from './MsSqlSchemaGenerator.js';
5
+ import type { MsSqlDriver } from './MsSqlDriver.js';
5
6
  export declare class MsSqlPlatform extends AbstractSqlPlatform {
6
7
  protected readonly schemaHelper: MsSqlSchemaHelper;
7
8
  protected readonly exceptionConverter: MsSqlExceptionConverter;
8
9
  /** @inheritDoc */
9
- lookupExtensions(orm: MikroORM): void;
10
+ lookupExtensions(orm: MikroORM<MsSqlDriver>): void;
10
11
  /** @inheritDoc */
11
12
  init(orm: MikroORM): void;
12
13
  getRollbackToSavepointSQL(savepointName: string): string;
@@ -50,7 +51,7 @@ export declare class MsSqlPlatform extends AbstractSqlPlatform {
50
51
  length?: number;
51
52
  }): string;
52
53
  validateMetadata(meta: EntityMetadata): void;
53
- getSearchJsonPropertyKey(path: string[], type: string, aliased: boolean, value?: unknown): string;
54
+ getSearchJsonPropertyKey(path: string[], type: string, aliased: boolean, value?: unknown): string | RawQueryFragment;
54
55
  normalizePrimaryKey<T extends number | string = number | string>(data: Primary<T> | IPrimaryKey | string): T;
55
56
  usesEnumCheckConstraints(): boolean;
56
57
  supportsMultipleCascadePaths(): boolean;
@@ -61,6 +62,8 @@ export declare class MsSqlPlatform extends AbstractSqlPlatform {
61
62
  escape(value: any): string;
62
63
  getSchemaGenerator(driver: IDatabaseDriver, em?: EntityManager): MsSqlSchemaGenerator;
63
64
  allowsComparingTuples(): boolean;
64
- getOrderByExpression(column: string, direction: QueryOrder): string[];
65
+ /** @internal MSSQL collation names are not quoted. */
66
+ quoteCollation(collation: string): string;
67
+ getOrderByExpression(column: string, direction: QueryOrder, collation?: string): string[];
65
68
  getDefaultClientUrl(): string;
66
69
  }
package/MsSqlPlatform.js CHANGED
@@ -1,4 +1,4 @@
1
- import { AbstractSqlPlatform, raw, Type, Utils, ALIAS_REPLACEMENT, DoubleType, FloatType, QueryOrder, RawQueryFragment, MsSqlNativeQueryBuilder, } from '@mikro-orm/knex';
1
+ import { AbstractSqlPlatform, ALIAS_REPLACEMENT, DoubleType, FloatType, MsSqlNativeQueryBuilder, QueryOrder, raw, RawQueryFragment, Type, } from '@mikro-orm/sql';
2
2
  // @ts-expect-error no types available
3
3
  import SqlString from 'tsqlstring';
4
4
  import { MsSqlSchemaHelper } from './MsSqlSchemaHelper.js';
@@ -33,7 +33,7 @@ export class MsSqlPlatform extends AbstractSqlPlatform {
33
33
  return true;
34
34
  }
35
35
  convertDateToJSValue(value) {
36
- /* v8 ignore next 3 */
36
+ /* v8 ignore next */
37
37
  if (typeof value === 'string') {
38
38
  return value;
39
39
  }
@@ -83,7 +83,7 @@ export class MsSqlPlatform extends AbstractSqlPlatform {
83
83
  return super.getVarcharTypeDeclarationSQL(column);
84
84
  }
85
85
  getEnumTypeDeclarationSQL(column) {
86
- if (column.items?.every(item => Utils.isString(item))) {
86
+ if (column.items?.every(item => typeof item === 'string')) {
87
87
  return Type.getType(UnicodeStringType).getColumnType({ length: 100, ...column }, this);
88
88
  }
89
89
  /* v8 ignore next */
@@ -101,7 +101,7 @@ export class MsSqlPlatform extends AbstractSqlPlatform {
101
101
  }
102
102
  getDefaultMappedType(type) {
103
103
  if (type.startsWith('float')) {
104
- const len = type.match(/float\((\d+)\)/)?.[1] ?? 24;
104
+ const len = /float\((\d+)\)/.exec(type)?.[1] ?? 24;
105
105
  return +len > 24 ? Type.getType(DoubleType) : Type.getType(FloatType);
106
106
  }
107
107
  const normalizedType = this.extractSimpleType(type);
@@ -130,9 +130,9 @@ export class MsSqlPlatform extends AbstractSqlPlatform {
130
130
  }
131
131
  validateMetadata(meta) {
132
132
  for (const prop of meta.props) {
133
- if ((prop.runtimeType === 'string' || ['string', 'nvarchar'].includes(prop.type))
134
- && !['uuid'].includes(prop.type)
135
- && !prop.columnTypes[0].startsWith('varchar')) {
133
+ if ((prop.runtimeType === 'string' || ['string', 'nvarchar'].includes(prop.type)) &&
134
+ !['uuid'].includes(prop.type) &&
135
+ !prop.columnTypes[0].startsWith('varchar')) {
136
136
  prop.customType ??= new UnicodeStringType();
137
137
  prop.customType.prop = prop;
138
138
  prop.customType.platform = this;
@@ -148,15 +148,15 @@ export class MsSqlPlatform extends AbstractSqlPlatform {
148
148
  boolean: 'bit',
149
149
  };
150
150
  const cast = (key) => raw(type in types ? `cast(${key} as ${types[type]})` : key);
151
- const quoteKey = (key) => key.match(/^[a-z]\w*$/i) ? key : `"${key}"`;
152
- /* v8 ignore next 3 */
151
+ const quoteKey = (key) => (/^[a-z]\w*$/i.exec(key) ? key : `"${key}"`);
152
+ /* v8 ignore next */
153
153
  if (path.length === 0) {
154
154
  return cast(`json_value(${root}, '$.${b.map(quoteKey).join('.')}')`);
155
155
  }
156
156
  return cast(`json_value(${root}, '$.${b.map(quoteKey).join('.')}')`);
157
157
  }
158
158
  normalizePrimaryKey(data) {
159
- /* v8 ignore next 3 */
159
+ /* v8 ignore next */
160
160
  if (data instanceof UnicodeString) {
161
161
  return data.value;
162
162
  }
@@ -189,25 +189,31 @@ export class MsSqlPlatform extends AbstractSqlPlatform {
189
189
  }
190
190
  return SqlString.escape(value);
191
191
  }
192
- /* v8 ignore next 3: kept for type inference only */
192
+ /* v8 ignore next: kept for type inference only */
193
193
  getSchemaGenerator(driver, em) {
194
194
  return new MsSqlSchemaGenerator(em ?? driver);
195
195
  }
196
196
  allowsComparingTuples() {
197
197
  return false;
198
198
  }
199
- getOrderByExpression(column, direction) {
199
+ /** @internal MSSQL collation names are not quoted. */
200
+ quoteCollation(collation) {
201
+ this.validateCollationName(collation);
202
+ return collation;
203
+ }
204
+ getOrderByExpression(column, direction, collation) {
205
+ const col = collation ? `${column} collate ${this.quoteCollation(collation)}` : column;
200
206
  switch (direction.toUpperCase()) {
201
207
  case QueryOrder.ASC_NULLS_FIRST:
202
- return [`case when ${column} is null then 0 else 1 end, ${column} asc`];
208
+ return [`case when ${column} is null then 0 else 1 end, ${col} asc`];
203
209
  case QueryOrder.ASC_NULLS_LAST:
204
- return [`case when ${column} is null then 1 else 0 end, ${column} asc`];
210
+ return [`case when ${column} is null then 1 else 0 end, ${col} asc`];
205
211
  case QueryOrder.DESC_NULLS_FIRST:
206
- return [`case when ${column} is null then 0 else 1 end, ${column} desc`];
212
+ return [`case when ${column} is null then 0 else 1 end, ${col} desc`];
207
213
  case QueryOrder.DESC_NULLS_LAST:
208
- return [`case when ${column} is null then 1 else 0 end, ${column} desc`];
214
+ return [`case when ${column} is null then 1 else 0 end, ${col} desc`];
209
215
  default:
210
- return [`${column} ${direction.toLowerCase()}`];
216
+ return [`${col} ${direction.toLowerCase()}`];
211
217
  }
212
218
  }
213
219
  getDefaultClientUrl() {
@@ -1,6 +1,6 @@
1
1
  import { type AnyEntity, type RequiredEntityData } from '@mikro-orm/core';
2
- import { type InsertQueryBuilder, QueryBuilder } from '@mikro-orm/knex';
2
+ import { type InsertQueryBuilder, QueryBuilder } from '@mikro-orm/sql';
3
3
  export declare class MsSqlQueryBuilder<Entity extends object = AnyEntity, RootAlias extends string = never, Hint extends string = never, Context extends object = never> extends QueryBuilder<Entity, RootAlias, Hint, Context> {
4
- insert(data: RequiredEntityData<Entity> | RequiredEntityData<Entity>[]): InsertQueryBuilder<Entity>;
4
+ insert(data: RequiredEntityData<Entity> | RequiredEntityData<Entity>[]): InsertQueryBuilder<Entity, RootAlias, Context>;
5
5
  private checkIdentityInsert;
6
6
  }
@@ -1,10 +1,10 @@
1
1
  import { QueryFlag, Utils } from '@mikro-orm/core';
2
- import { QueryBuilder } from '@mikro-orm/knex';
2
+ import { QueryBuilder } from '@mikro-orm/sql';
3
3
  export class MsSqlQueryBuilder extends QueryBuilder {
4
4
  insert(data) {
5
5
  this.checkIdentityInsert(data);
6
- if (!this.flags.has(QueryFlag.IDENTITY_INSERT) && this.metadata.has(this.mainAlias.entityName)) {
7
- const meta = this.metadata.find(this.mainAlias.entityName);
6
+ if (!this.hasFlag(QueryFlag.IDENTITY_INSERT) && this.metadata.has(this.mainAlias.entityName)) {
7
+ const meta = this.mainAlias.meta;
8
8
  if (meta.hasTriggers) {
9
9
  this.setFlag(QueryFlag.OUTPUT_TABLE);
10
10
  }
@@ -12,11 +12,8 @@ export class MsSqlQueryBuilder extends QueryBuilder {
12
12
  return super.insert(data);
13
13
  }
14
14
  checkIdentityInsert(data) {
15
- const meta = this.metadata.find(this.mainAlias.entityName);
16
- if (!meta) {
17
- return;
18
- }
19
- const dataKeys = Utils.unique(Utils.asArray(data).flatMap(Object.keys));
15
+ const meta = this.mainAlias.meta;
16
+ const dataKeys = Utils.unique(Utils.asArray(data).flatMap(d => Utils.keys(d)));
20
17
  const hasAutoincrement = dataKeys.some(x => meta.properties[x]?.autoincrement);
21
18
  if (hasAutoincrement) {
22
19
  this.setFlag(QueryFlag.IDENTITY_INSERT);
@@ -1,6 +1,7 @@
1
- import { type ClearDatabaseOptions, type DropSchemaOptions, type MikroORM, SchemaGenerator } from '@mikro-orm/knex';
1
+ import { type ClearDatabaseOptions, type DropSchemaOptions, type MikroORM, SchemaGenerator } from '@mikro-orm/sql';
2
+ import type { MsSqlDriver } from './MsSqlDriver.js';
2
3
  export declare class MsSqlSchemaGenerator extends SchemaGenerator {
3
- static register(orm: MikroORM): void;
4
- clearDatabase(options?: ClearDatabaseOptions): Promise<void>;
4
+ static register(orm: MikroORM<MsSqlDriver>): void;
5
+ clear(options?: ClearDatabaseOptions): Promise<void>;
5
6
  getDropSchemaSQL(options?: Omit<DropSchemaOptions, 'dropDb'>): Promise<string>;
6
7
  }
@@ -1,17 +1,17 @@
1
- import { SchemaGenerator } from '@mikro-orm/knex';
1
+ import { SchemaGenerator } from '@mikro-orm/sql';
2
2
  export class MsSqlSchemaGenerator extends SchemaGenerator {
3
3
  static register(orm) {
4
4
  orm.config.registerExtension('@mikro-orm/schema-generator', () => new MsSqlSchemaGenerator(orm.em));
5
5
  }
6
- async clearDatabase(options) {
6
+ async clear(options) {
7
7
  // truncate by default, so no value is considered as true
8
- /* v8 ignore next 3 */
8
+ /* v8 ignore next */
9
9
  if (options?.truncate === false) {
10
- return super.clearDatabase(options);
10
+ return super.clear(options);
11
11
  }
12
12
  // https://stackoverflow.com/questions/253849/cannot-truncate-table-because-it-is-being-referenced-by-a-foreign-key-constraint
13
13
  for (const meta of this.getOrderedMetadata(options?.schema).reverse()) {
14
- const res = await this.driver.nativeDelete(meta.className, {}, options);
14
+ const res = await this.driver.nativeDelete(meta.class, {}, options);
15
15
  if (meta.getPrimaryProps().some(pk => pk.autoincrement)) {
16
16
  const tableName = this.driver.getTableName(meta, { schema: options?.schema }, false);
17
17
  await this.execute(`dbcc checkident ('${tableName}', reseed, ${res.affectedRows > 0 ? 0 : 1})`, {
@@ -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 Type } from '@mikro-orm/knex';
1
+ import { type AbstractSqlConnection, type CheckDef, type Column, type DatabaseSchema, type DatabaseTable, type Dictionary, type ForeignKey, type IndexDef, SchemaHelper, type Table, type TableDifference, type Type } from '@mikro-orm/sql';
2
2
  export declare class MsSqlSchemaHelper extends SchemaHelper {
3
3
  static readonly DEFAULT_VALUES: {
4
4
  true: string[];
@@ -10,6 +10,8 @@ export declare class MsSqlSchemaHelper extends SchemaHelper {
10
10
  enableForeignKeysSQL(): string;
11
11
  getDatabaseExistsSQL(name: string): string;
12
12
  getListTablesSQL(): string;
13
+ getListViewsSQL(): string;
14
+ loadViews(schema: DatabaseSchema, connection: AbstractSqlConnection): Promise<void>;
13
15
  getNamespaces(connection: AbstractSqlConnection): Promise<string[]>;
14
16
  normalizeDefaultValue(defaultValue: string, length: number, defaultValues?: Dictionary<string[]>, stripQuotes?: boolean): string | number;
15
17
  getAllColumns(connection: AbstractSqlConnection, tablesBySchemas: Map<string | undefined, Table[]>): Promise<Dictionary<Column[]>>;
@@ -32,9 +34,18 @@ export declare class MsSqlSchemaHelper extends SchemaHelper {
32
34
  createTableColumn(column: Column, table: DatabaseTable, changedProperties?: Set<string>): string | undefined;
33
35
  alterTableColumn(column: Column, table: DatabaseTable, changedProperties: Set<string>): string[];
34
36
  getCreateIndexSQL(tableName: string, index: IndexDef, partialExpression?: boolean): string;
37
+ /**
38
+ * Build the column list for a MSSQL index.
39
+ */
40
+ protected getIndexColumns(index: IndexDef): string;
41
+ /**
42
+ * Get MSSQL-specific index WITH options like fill factor.
43
+ */
44
+ private getMsSqlIndexSuffix;
35
45
  createIndex(index: IndexDef, table: DatabaseTable, createPrimary?: boolean): string;
36
46
  dropForeignKey(tableName: string, constraintName: string): string;
37
47
  dropTableIfExists(name: string, schema?: string): string;
48
+ dropViewIfExists(name: string, schema?: string): string;
38
49
  getAddColumnsSQL(table: DatabaseTable, columns: Column[]): string[];
39
50
  appendComments(table: DatabaseTable): string[];
40
51
  inferLengthFromColumnType(type: string): number | undefined;
@@ -1,9 +1,9 @@
1
- import { EnumType, SchemaHelper, StringType, TextType, Utils, } from '@mikro-orm/knex';
1
+ import { EnumType, SchemaHelper, StringType, TextType, Utils, } from '@mikro-orm/sql';
2
2
  import { UnicodeStringType } from './UnicodeStringType.js';
3
3
  export class MsSqlSchemaHelper extends SchemaHelper {
4
4
  static DEFAULT_VALUES = {
5
- 'true': ['1'],
6
- 'false': ['0'],
5
+ true: ['1'],
6
+ false: ['0'],
7
7
  'getdate()': ['current_timestamp'],
8
8
  };
9
9
  getManagementDbName() {
@@ -25,21 +25,39 @@ export class MsSqlSchemaHelper extends SchemaHelper {
25
25
  left join sys.extended_properties ep on ep.major_id = t.id and ep.name = 'MS_Description' and ep.minor_id = 0
26
26
  order by schema_name(t2.schema_id), t.name`;
27
27
  }
28
+ getListViewsSQL() {
29
+ return `select v.name as view_name, schema_name(v.schema_id) as schema_name, m.definition as view_definition
30
+ from sys.views v
31
+ inner join sys.sql_modules m on v.object_id = m.object_id
32
+ order by schema_name(v.schema_id), v.name`;
33
+ }
34
+ async loadViews(schema, connection) {
35
+ const views = await connection.execute(this.getListViewsSQL());
36
+ for (const view of views) {
37
+ // Extract SELECT statement from CREATE VIEW ... AS SELECT ...
38
+ const match = /\bAS\s+(.+)$/is.exec(view.view_definition);
39
+ const definition = match?.[1]?.trim();
40
+ if (definition) {
41
+ const schemaName = view.schema_name === this.platform.getDefaultSchemaName() ? undefined : view.schema_name;
42
+ schema.addView(view.view_name, schemaName, definition);
43
+ }
44
+ }
45
+ }
28
46
  async getNamespaces(connection) {
29
47
  const sql = `select name as schema_name from sys.schemas order by name`;
30
48
  const res = await connection.execute(sql);
31
49
  return res.map(row => row.schema_name);
32
50
  }
33
51
  normalizeDefaultValue(defaultValue, length, defaultValues = {}, stripQuotes = false) {
34
- let match = defaultValue?.match(/^\((.*)\)$/);
52
+ let match = /^\((.*)\)$/.exec(defaultValue);
35
53
  if (match) {
36
54
  defaultValue = match[1];
37
55
  }
38
- match = defaultValue?.match(/^\((.*)\)$/);
56
+ match = /^\((.*)\)$/.exec(defaultValue);
39
57
  if (match) {
40
58
  defaultValue = match[1];
41
59
  }
42
- match = defaultValue?.match(/^'(.*)'$/);
60
+ match = /^'(.*)'$/.exec(defaultValue);
43
61
  if (stripQuotes && match) {
44
62
  defaultValue = match[1];
45
63
  }
@@ -66,10 +84,10 @@ export class MsSqlSchemaHelper extends SchemaHelper {
66
84
  left join sys.computed_columns cmp on cmp.name = ic.column_name and cmp.object_id = object_id(ic.table_schema + '.' + ic.table_name)
67
85
  left join sys.extended_properties t4 on t4.major_id = object_id(ic.table_schema + '.' + ic.table_name) and t4.name = 'MS_Description' and t4.minor_id = sc.column_id
68
86
  left join sys.default_constraints t5 on sc.default_object_id = t5.object_id
69
- 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 ')})
87
+ 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 ')})
70
88
  order by ordinal_position`;
71
89
  const allColumns = await connection.execute(sql);
72
- const str = (val) => val != null ? '' + val : val;
90
+ const str = (val) => (val != null ? '' + val : val);
73
91
  const ret = {};
74
92
  for (const col of allColumns) {
75
93
  const mappedType = this.platform.getMappedType(col.data_type);
@@ -77,7 +95,9 @@ export class MsSqlSchemaHelper extends SchemaHelper {
77
95
  const increments = col.is_identity === 1 && connection.getPlatform().isNumericColumn(mappedType);
78
96
  const key = this.getTableKey(col);
79
97
  /* v8 ignore next */
80
- const generated = col.generation_expression ? `${col.generation_expression}${col.is_persisted ? ' persisted' : ''}` : undefined;
98
+ const generated = col.generation_expression
99
+ ? `${col.generation_expression}${col.is_persisted ? ' persisted' : ''}`
100
+ : undefined;
81
101
  let type = col.data_type;
82
102
  if (['varchar', 'nvarchar', 'char', 'nchar', 'varbinary'].includes(col.data_type)) {
83
103
  col.length = col.character_maximum_length;
@@ -97,7 +117,9 @@ export class MsSqlSchemaHelper extends SchemaHelper {
97
117
  ret[key] ??= [];
98
118
  ret[key].push({
99
119
  name: col.column_name,
100
- type: this.platform.isNumericColumn(mappedType) ? col.data_type.replace(/ unsigned$/, '').replace(/\(\d+\)$/, '') : type,
120
+ type: this.platform.isNumericColumn(mappedType)
121
+ ? col.data_type.replace(/ unsigned$/, '').replace(/\(\d+\)$/, '')
122
+ : type,
101
123
  mappedType,
102
124
  unsigned: col.data_type.endsWith(' unsigned'),
103
125
  length: col.length,
@@ -120,26 +142,52 @@ export class MsSqlSchemaHelper extends SchemaHelper {
120
142
  ind.is_primary_key as is_primary_key,
121
143
  col.name as column_name,
122
144
  schema_name(t.schema_id) as schema_name,
123
- (case when filter_definition is not null then concat('where ', filter_definition) else null end) as expression
145
+ (case when filter_definition is not null then concat('where ', filter_definition) else null end) as expression,
146
+ ind.is_disabled as is_disabled,
147
+ ind.type as index_type,
148
+ ind.fill_factor as fill_factor,
149
+ ic.is_included_column as is_included_column,
150
+ ic.is_descending_key as is_descending_key
124
151
  from sys.indexes ind
125
152
  inner join sys.index_columns ic on ind.object_id = ic.object_id and ind.index_id = ic.index_id
126
153
  inner join sys.columns col on ic.object_id = col.object_id and ic.column_id = col.column_id
127
154
  inner join sys.tables t on ind.object_id = t.object_id
128
155
  where
129
156
  (${[...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 ')})
130
- order by t.name, ind.name, ind.index_id`;
157
+ order by t.name, ind.name, ic.is_included_column, ic.key_ordinal`;
131
158
  const allIndexes = await connection.execute(sql);
132
159
  const ret = {};
133
160
  for (const index of allIndexes) {
134
161
  const key = this.getTableKey(index);
162
+ const isIncluded = index.is_included_column;
135
163
  const indexDef = {
136
- columnNames: [index.column_name],
164
+ columnNames: isIncluded ? [] : [index.column_name],
137
165
  keyName: index.index_name,
138
166
  unique: index.is_unique,
139
167
  primary: index.is_primary_key,
140
168
  constraint: index.is_unique,
141
169
  };
142
- if (!index.column_name || index.column_name.match(/[(): ,"'`]/) || index.expression?.match(/where /i)) {
170
+ // Capture INCLUDE columns
171
+ if (isIncluded) {
172
+ indexDef.include = [index.column_name];
173
+ }
174
+ // Capture sort order for key columns
175
+ if (!isIncluded && index.is_descending_key) {
176
+ indexDef.columns = [{ name: index.column_name, sort: 'DESC' }];
177
+ }
178
+ // Capture disabled flag
179
+ if (index.is_disabled) {
180
+ indexDef.disabled = true;
181
+ }
182
+ // Capture clustered flag (type 1 = clustered)
183
+ if (index.index_type === 1 && !index.is_primary_key) {
184
+ indexDef.clustered = true;
185
+ }
186
+ // Capture fill factor (0 means default, so only set if non-zero)
187
+ if (index.fill_factor > 0) {
188
+ indexDef.fillFactor = index.fill_factor;
189
+ }
190
+ if (index.column_name?.match(/[(): ,"'`]/) || index.expression?.match(/where /i)) {
143
191
  indexDef.expression = index.expression; // required for the `getCreateIndexSQL()` call
144
192
  indexDef.expression = this.getCreateIndexSQL(index.table_name, indexDef, !!index.expression);
145
193
  }
@@ -193,7 +241,9 @@ export class MsSqlSchemaHelper extends SchemaHelper {
193
241
  /* v8 ignore next */
194
242
  const hasItems = (items?.length ?? 0) > 0;
195
243
  if (item.columnName && hasItems) {
196
- items = items.map(val => val.trim().replace(`[${item.columnName}]=`, '').match(/^\(?'(.*)'/)?.[1]).filter(Boolean);
244
+ items = items
245
+ .map(val => /^\(?'(.*)'/.exec(val.trim().replace(`[${item.columnName}]=`, ''))?.[1])
246
+ .filter(Boolean);
197
247
  if (items.length > 0) {
198
248
  o[item.columnName] = items.reverse();
199
249
  }
@@ -266,7 +316,7 @@ export class MsSqlSchemaHelper extends SchemaHelper {
266
316
  }
267
317
  // convert to string first if it's not already a string or has a smaller length
268
318
  const type = this.platform.extractSimpleType(col.fromColumn.type);
269
- if (!['varchar', 'nvarchar', 'varbinary'].includes(type) || (col.fromColumn.length < col.column.length)) {
319
+ if (!['varchar', 'nvarchar', 'varbinary'].includes(type) || col.fromColumn.length < col.column.length) {
270
320
  ret.push(`alter table ${quotedName} alter column [${col.oldColumnName}] nvarchar(max)`);
271
321
  }
272
322
  }
@@ -328,18 +378,21 @@ export class MsSqlSchemaHelper extends SchemaHelper {
328
378
  }
329
379
  const i = globalThis.idx;
330
380
  globalThis.idx++;
331
- constraints.push(`declare @constraint${i} varchar(100) = (select default_constraints.name from sys.all_columns`
332
- + ' join sys.tables on all_columns.object_id = tables.object_id'
333
- + ' join sys.schemas on tables.schema_id = schemas.schema_id'
334
- + ' join sys.default_constraints on all_columns.default_object_id = default_constraints.object_id'
335
- + ` where schemas.name = '${schemaName}' and tables.name = '${tableName}' and all_columns.name = '${column.name}')`
336
- + ` if @constraint${i} is not null exec('alter table ${tableNameRaw} drop constraint ' + @constraint${i})`);
381
+ constraints.push(`declare @constraint${i} varchar(100) = (select default_constraints.name from sys.all_columns` +
382
+ ' join sys.tables on all_columns.object_id = tables.object_id' +
383
+ ' join sys.schemas on tables.schema_id = schemas.schema_id' +
384
+ ' join sys.default_constraints on all_columns.default_object_id = default_constraints.object_id' +
385
+ ` where schemas.name = '${schemaName}' and tables.name = '${tableName}' and all_columns.name = '${column.name}')` +
386
+ ` if @constraint${i} is not null exec('alter table ${tableNameRaw} drop constraint ' + @constraint${i})`);
337
387
  }
338
388
  return constraints;
339
389
  }
340
390
  getRenameColumnSQL(tableName, oldColumnName, to, schemaName) {
341
391
  /* v8 ignore next */
342
- const oldName = (schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName + '.' + oldColumnName;
392
+ const oldName = (schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') +
393
+ tableName +
394
+ '.' +
395
+ oldColumnName;
343
396
  const columnName = this.platform.quoteValue(to.name);
344
397
  return `exec sp_rename ${this.platform.quoteValue(oldName)}, ${columnName}, 'COLUMN'`;
345
398
  }
@@ -348,7 +401,10 @@ export class MsSqlSchemaHelper extends SchemaHelper {
348
401
  const primaryKey = !changedProperties && !this.hasNonDefaultPrimaryKeyName(table);
349
402
  const columnType = column.generated ? `as ${column.generated}` : column.type;
350
403
  const col = [this.quote(column.name)];
351
- if (column.autoincrement && !column.generated && !compositePK && (!changedProperties || changedProperties.has('autoincrement') || changedProperties.has('type'))) {
404
+ if (column.autoincrement &&
405
+ !column.generated &&
406
+ !compositePK &&
407
+ (!changedProperties || changedProperties.has('autoincrement') || changedProperties.has('type'))) {
352
408
  col.push(column.mappedType.getColumnType({ autoincrement: true }, this.platform));
353
409
  }
354
410
  else {
@@ -357,10 +413,15 @@ export class MsSqlSchemaHelper extends SchemaHelper {
357
413
  Utils.runIfNotEmpty(() => col.push('identity(1,1)'), column.autoincrement);
358
414
  Utils.runIfNotEmpty(() => col.push('null'), column.nullable);
359
415
  Utils.runIfNotEmpty(() => col.push('not null'), !column.nullable && !column.generated);
360
- if (column.autoincrement && !column.generated && !compositePK && (!changedProperties || changedProperties.has('autoincrement') || changedProperties.has('type'))) {
416
+ if (column.autoincrement &&
417
+ !column.generated &&
418
+ !compositePK &&
419
+ (!changedProperties || changedProperties.has('autoincrement') || changedProperties.has('type'))) {
361
420
  Utils.runIfNotEmpty(() => col.push('primary key'), primaryKey && column.primary);
362
421
  }
363
- const useDefault = changedProperties ? false : column.default != null && column.default !== 'null' && !column.autoincrement;
422
+ const useDefault = changedProperties
423
+ ? false
424
+ : column.default != null && column.default !== 'null' && !column.autoincrement;
364
425
  const defaultName = this.platform.getConfig().getNamingStrategy().indexName(table.name, [column.name], 'default');
365
426
  Utils.runIfNotEmpty(() => col.push(`constraint ${this.quote(defaultName)} default ${column.default}`), useDefault);
366
427
  return col.join(' ');
@@ -382,17 +443,64 @@ export class MsSqlSchemaHelper extends SchemaHelper {
382
443
  return parts;
383
444
  }
384
445
  getCreateIndexSQL(tableName, index, partialExpression = false) {
385
- /* v8 ignore next 3 */
446
+ /* v8 ignore next */
386
447
  if (index.expression && !partialExpression) {
387
448
  return index.expression;
388
449
  }
450
+ if (index.fillFactor != null && (index.fillFactor < 0 || index.fillFactor > 100)) {
451
+ throw new Error(`fillFactor must be between 0 and 100, got ${index.fillFactor} for index '${index.keyName}'`);
452
+ }
389
453
  const keyName = this.quote(index.keyName);
390
- const defer = index.deferMode ? ` deferrable initially ${index.deferMode}` : '';
391
- const sql = `create ${index.unique ? 'unique ' : ''}index ${keyName} on ${this.quote(tableName)} `;
454
+ // Only add clustered keyword when explicitly requested, otherwise omit (defaults to nonclustered)
455
+ const clustered = index.clustered ? 'clustered ' : '';
456
+ let sql = `create ${index.unique ? 'unique ' : ''}${clustered}index ${keyName} on ${this.quote(tableName)} `;
392
457
  if (index.expression && partialExpression) {
393
- return `${sql}(${index.expression})${defer}`;
458
+ return sql + `(${index.expression})` + this.getMsSqlIndexSuffix(index);
459
+ }
460
+ // Build column list with advanced options
461
+ const columns = this.getIndexColumns(index);
462
+ sql += `(${columns})`;
463
+ // Add INCLUDE clause for covering indexes
464
+ if (index.include?.length) {
465
+ sql += ` include (${index.include.map(c => this.quote(c)).join(', ')})`;
394
466
  }
395
- return super.getCreateIndexSQL(tableName, index);
467
+ sql += this.getMsSqlIndexSuffix(index);
468
+ // Disabled indexes need to be created first, then disabled
469
+ if (index.disabled) {
470
+ sql += `;\nalter index ${keyName} on ${this.quote(tableName)} disable`;
471
+ }
472
+ return sql;
473
+ }
474
+ /**
475
+ * Build the column list for a MSSQL index.
476
+ */
477
+ getIndexColumns(index) {
478
+ if (index.columns?.length) {
479
+ return index.columns
480
+ .map(col => {
481
+ let colDef = this.quote(col.name);
482
+ // MSSQL supports sort order
483
+ if (col.sort) {
484
+ colDef += ` ${col.sort}`;
485
+ }
486
+ return colDef;
487
+ })
488
+ .join(', ');
489
+ }
490
+ return index.columnNames.map(c => this.quote(c)).join(', ');
491
+ }
492
+ /**
493
+ * Get MSSQL-specific index WITH options like fill factor.
494
+ */
495
+ getMsSqlIndexSuffix(index) {
496
+ const withOptions = [];
497
+ if (index.fillFactor != null) {
498
+ withOptions.push(`fillfactor = ${index.fillFactor}`);
499
+ }
500
+ if (withOptions.length > 0) {
501
+ return ` with (${withOptions.join(', ')})`;
502
+ }
503
+ return '';
396
504
  }
397
505
  createIndex(index, table, createPrimary = false) {
398
506
  if (index.primary) {
@@ -401,13 +509,17 @@ export class MsSqlSchemaHelper extends SchemaHelper {
401
509
  if (index.expression) {
402
510
  return index.expression;
403
511
  }
404
- const quotedTableName = table.getQuotedName();
405
- if (index.unique) {
406
- const nullable = index.columnNames.some(column => table.getColumn(column)?.nullable);
407
- const where = nullable ? ' where ' + index.columnNames.map(c => `${this.quote(c)} is not null`).join(' and ') : '';
408
- return `create unique index ${this.quote(index.keyName)} on ${quotedTableName} (${index.columnNames.map(c => this.quote(c)).join(', ')})${where}`;
512
+ const needsWhereClause = index.unique && index.columnNames.some(column => table.getColumn(column)?.nullable);
513
+ if (!needsWhereClause) {
514
+ return this.getCreateIndexSQL(table.getShortestName(), index);
515
+ }
516
+ // Generate without disabled suffix, insert WHERE clause, then re-add disabled
517
+ let sql = this.getCreateIndexSQL(table.getShortestName(), { ...index, disabled: false });
518
+ sql += ' where ' + index.columnNames.map(c => `${this.quote(c)} is not null`).join(' and ');
519
+ if (index.disabled) {
520
+ sql += `;\nalter index ${this.quote(index.keyName)} on ${table.getQuotedName()} disable`;
409
521
  }
410
- return super.createIndex(index, table);
522
+ return sql;
411
523
  }
412
524
  dropForeignKey(tableName, constraintName) {
413
525
  return `alter table ${this.quote(tableName)} drop constraint ${this.quote(constraintName)}`;
@@ -418,10 +530,16 @@ export class MsSqlSchemaHelper extends SchemaHelper {
418
530
  }
419
531
  return `if object_id('${this.quote(schema, name)}', 'U') is not null drop table ${this.quote(schema, name)}`;
420
532
  }
533
+ dropViewIfExists(name, schema) {
534
+ const viewName = this.quote(this.getTableName(name, schema));
535
+ return `if object_id('${viewName}', 'V') is not null drop view ${viewName}`;
536
+ }
421
537
  getAddColumnsSQL(table, columns) {
422
- const adds = columns.map(column => {
423
- return `${this.createTableColumn(column, table)}`;
424
- }).join(', ');
538
+ const adds = columns
539
+ .map(column => {
540
+ return this.createTableColumn(column, table);
541
+ })
542
+ .join(', ');
425
543
  return [`alter table ${table.getQuotedName()} add ${adds}`];
426
544
  }
427
545
  appendComments(table) {
@@ -448,7 +566,7 @@ else
448
566
  return sql;
449
567
  }
450
568
  inferLengthFromColumnType(type) {
451
- const match = type.match(/^(\w+)\s*\(\s*(-?\d+|max)\s*\)/);
569
+ const match = /^(\w+)\s*\(\s*(-?\d+|max)\s*\)/.exec(type);
452
570
  if (!match) {
453
571
  return;
454
572
  }
@@ -458,7 +576,10 @@ else
458
576
  return +match[2];
459
577
  }
460
578
  wrap(val, type) {
461
- const stringType = type instanceof StringType || type instanceof TextType || type instanceof EnumType || type instanceof UnicodeStringType;
579
+ const stringType = type instanceof StringType ||
580
+ type instanceof TextType ||
581
+ type instanceof EnumType ||
582
+ type instanceof UnicodeStringType;
462
583
  return typeof val === 'string' && val.length > 0 && stringType ? this.platform.quoteValue(val) : val;
463
584
  }
464
585
  }
package/README.md CHANGED
@@ -2,14 +2,14 @@
2
2
  <a href="https://mikro-orm.io"><img src="https://raw.githubusercontent.com/mikro-orm/mikro-orm/master/docs/static/img/logo-readme.svg?sanitize=true" alt="MikroORM" /></a>
3
3
  </h1>
4
4
 
5
- TypeScript ORM for Node.js based on Data Mapper, [Unit of Work](https://mikro-orm.io/docs/unit-of-work/) and [Identity Map](https://mikro-orm.io/docs/identity-map/) patterns. Supports MongoDB, MySQL, MariaDB, PostgreSQL and SQLite (including libSQL) databases.
5
+ TypeScript ORM for Node.js based on Data Mapper, [Unit of Work](https://mikro-orm.io/docs/unit-of-work/) and [Identity Map](https://mikro-orm.io/docs/identity-map/) patterns. Supports MongoDB, MySQL, MariaDB, PostgreSQL, SQLite (including libSQL), MSSQL and Oracle databases.
6
6
 
7
7
  > Heavily inspired by [Doctrine](https://www.doctrine-project.org/) and [Hibernate](https://hibernate.org/).
8
8
 
9
- [![NPM version](https://img.shields.io/npm/v/@mikro-orm/core.svg)](https://www.npmjs.com/package/@mikro-orm/core)
10
- [![NPM dev version](https://img.shields.io/npm/v/@mikro-orm/core/next.svg)](https://www.npmjs.com/package/@mikro-orm/core)
9
+ [![NPM version](https://img.shields.io/npm/v/@mikro-orm/core.svg)](https://npmx.dev/package/@mikro-orm/core)
10
+ [![NPM dev version](https://img.shields.io/npm/v/@mikro-orm/core/next.svg)](https://npmx.dev/package/@mikro-orm/core)
11
11
  [![Chat on discord](https://img.shields.io/discord/1214904142443839538?label=discord&color=blue)](https://discord.gg/w8bjxFHS7X)
12
- [![Downloads](https://img.shields.io/npm/dm/@mikro-orm/core.svg)](https://www.npmjs.com/package/@mikro-orm/core)
12
+ [![Downloads](https://img.shields.io/npm/dm/@mikro-orm/core.svg)](https://npmx.dev/package/@mikro-orm/core)
13
13
  [![Coverage Status](https://img.shields.io/coveralls/mikro-orm/mikro-orm.svg)](https://coveralls.io/r/mikro-orm/mikro-orm?branch=master)
14
14
  [![Build Status](https://github.com/mikro-orm/mikro-orm/workflows/tests/badge.svg?branch=master)](https://github.com/mikro-orm/mikro-orm/actions?workflow=tests)
15
15
 
@@ -181,6 +181,7 @@ yarn add @mikro-orm/core @mikro-orm/mysql # for mysql/mariadb
181
181
  yarn add @mikro-orm/core @mikro-orm/mariadb # for mysql/mariadb
182
182
  yarn add @mikro-orm/core @mikro-orm/postgresql # for postgresql
183
183
  yarn add @mikro-orm/core @mikro-orm/mssql # for mssql
184
+ yarn add @mikro-orm/core @mikro-orm/oracledb # for oracle
184
185
  yarn add @mikro-orm/core @mikro-orm/sqlite # for sqlite
185
186
  yarn add @mikro-orm/core @mikro-orm/libsql # for libsql
186
187
  ```
@@ -381,6 +382,8 @@ See also the list of contributors who [participated](https://github.com/mikro-or
381
382
 
382
383
  Please ⭐️ this repository if this project helped you!
383
384
 
385
+ > If you'd like to support my open-source work, consider sponsoring me directly at [github.com/sponsors/b4nan](https://github.com/sponsors/b4nan).
386
+
384
387
  ## 📝 License
385
388
 
386
389
  Copyright © 2018 [Martin Adámek](https://github.com/b4nan).
@@ -23,7 +23,7 @@ export class UnicodeStringType extends Type {
23
23
  return `nvarchar(${length})`;
24
24
  }
25
25
  convertToJSValue(value) {
26
- /* v8 ignore next 3 */
26
+ /* v8 ignore next */
27
27
  if (value instanceof UnicodeString) {
28
28
  return value.value;
29
29
  }
package/index.d.ts CHANGED
@@ -1,8 +1,7 @@
1
- export * from '@mikro-orm/knex';
1
+ export * from '@mikro-orm/sql';
2
2
  export * from './MsSqlConnection.js';
3
3
  export * from './MsSqlDriver.js';
4
4
  export * from './MsSqlPlatform.js';
5
5
  export * from './MsSqlSchemaHelper.js';
6
- export * from './MsSqlExceptionConverter.js';
7
6
  export * from './UnicodeStringType.js';
8
- export { MsSqlMikroORM as MikroORM, MsSqlOptions as Options, defineMsSqlConfig as defineConfig, } from './MsSqlMikroORM.js';
7
+ export { MsSqlMikroORM as MikroORM, type MsSqlOptions as Options, defineMsSqlConfig as defineConfig, } from './MsSqlMikroORM.js';
package/index.js CHANGED
@@ -1,8 +1,7 @@
1
- export * from '@mikro-orm/knex';
1
+ export * from '@mikro-orm/sql';
2
2
  export * from './MsSqlConnection.js';
3
3
  export * from './MsSqlDriver.js';
4
4
  export * from './MsSqlPlatform.js';
5
5
  export * from './MsSqlSchemaHelper.js';
6
- export * from './MsSqlExceptionConverter.js';
7
6
  export * from './UnicodeStringType.js';
8
7
  export { MsSqlMikroORM as MikroORM, defineMsSqlConfig as defineConfig, } from './MsSqlMikroORM.js';
package/package.json CHANGED
@@ -1,66 +1,65 @@
1
1
  {
2
2
  "name": "@mikro-orm/mssql",
3
- "type": "module",
4
- "version": "7.0.0-dev.33",
3
+ "version": "7.0.0-dev.330",
5
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.",
6
- "exports": {
7
- "./package.json": "./package.json",
8
- ".": "./index.js"
9
- },
10
- "repository": {
11
- "type": "git",
12
- "url": "git+ssh://git@github.com/mikro-orm/mikro-orm.git"
13
- },
14
5
  "keywords": [
15
- "orm",
6
+ "data-mapper",
7
+ "ddd",
8
+ "entity",
9
+ "identity-map",
10
+ "javascript",
11
+ "js",
12
+ "mariadb",
13
+ "mikro-orm",
16
14
  "mongo",
17
15
  "mongodb",
18
16
  "mysql",
19
- "mariadb",
17
+ "orm",
20
18
  "postgresql",
21
19
  "sqlite",
22
20
  "sqlite3",
23
21
  "ts",
24
22
  "typescript",
25
- "js",
26
- "javascript",
27
- "entity",
28
- "ddd",
29
- "mikro-orm",
30
- "unit-of-work",
31
- "data-mapper",
32
- "identity-map"
23
+ "unit-of-work"
33
24
  ],
34
- "author": "Martin Adámek",
35
- "license": "MIT",
25
+ "homepage": "https://mikro-orm.io",
36
26
  "bugs": {
37
27
  "url": "https://github.com/mikro-orm/mikro-orm/issues"
38
28
  },
39
- "homepage": "https://mikro-orm.io",
40
- "engines": {
41
- "node": ">= 22.11.0"
29
+ "license": "MIT",
30
+ "author": "Martin Adámek",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+ssh://git@github.com/mikro-orm/mikro-orm.git"
34
+ },
35
+ "type": "module",
36
+ "exports": {
37
+ "./package.json": "./package.json",
38
+ ".": "./index.js"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
42
  },
43
43
  "scripts": {
44
- "build": "yarn clean && yarn compile && yarn copy",
44
+ "build": "yarn compile && yarn copy",
45
45
  "clean": "yarn run -T rimraf ./dist",
46
46
  "compile": "yarn run -T tsc -p tsconfig.build.json",
47
47
  "copy": "node ../../scripts/copy.mjs"
48
48
  },
49
- "publishConfig": {
50
- "access": "public"
51
- },
52
49
  "dependencies": {
53
- "@mikro-orm/knex": "7.0.0-dev.33",
50
+ "@mikro-orm/sql": "7.0.0-dev.330",
51
+ "kysely": "0.28.11",
54
52
  "tarn": "3.0.2",
55
- "tedious": "19.0.0",
53
+ "tedious": "19.2.1",
56
54
  "tsqlstring": "1.0.1"
57
55
  },
58
56
  "devDependencies": {
59
- "@mikro-orm/core": "^6.5.7",
60
- "kysely": "0.28.7"
57
+ "@mikro-orm/core": "^6.6.9"
61
58
  },
62
59
  "peerDependencies": {
63
- "@mikro-orm/core": "7.0.0-dev.33",
64
- "kysely": "*"
60
+ "@mikro-orm/core": "7.0.0-dev.330"
61
+ },
62
+ "engines": {
63
+ "node": ">= 22.17.0"
65
64
  }
66
65
  }