@mikro-orm/mssql 7.2.2-dev.1 → 7.2.2-dev.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -143,19 +143,22 @@ export class MsSqlConnection extends AbstractSqlConnection {
143
143
  return super.commit(ctx, eventBroadcaster);
144
144
  }
145
145
  transformRawResult(res, method) {
146
+ const lastRow = res.rows.at(-1);
147
+ const hasOutputCount = lastRow && '__mikro_orm_row_count__' in lastRow && Object.keys(lastRow).length === 1;
148
+ const outputRows = hasOutputCount ? res.rows.slice(0, -1) : res.rows;
146
149
  if (method === 'get') {
147
- return res.rows[0];
150
+ return outputRows[0];
148
151
  }
149
152
  if (method === 'all') {
150
- return res.rows;
153
+ return outputRows;
151
154
  }
152
- const rowCount = res.rows.length;
153
- const hasEmptyCount = rowCount === 1 && '' in res.rows[0];
154
- const emptyRow = hasEmptyCount && Number(res.rows[0]['']);
155
+ const hasEmptyCount = lastRow && Object.keys(lastRow).length === 1 && '' in lastRow;
156
+ const count = hasOutputCount ? lastRow.__mikro_orm_row_count__ : hasEmptyCount ? lastRow[''] : res.numAffectedRows;
157
+ const rows = hasEmptyCount ? outputRows.slice(0, -1) : outputRows;
155
158
  return {
156
- affectedRows: hasEmptyCount ? emptyRow : Number(res.numAffectedRows),
157
- row: res.rows[0],
158
- rows: res.rows,
159
+ affectedRows: Number(count),
160
+ row: rows[0],
161
+ rows,
159
162
  };
160
163
  }
161
164
  }
package/MsSqlDriver.js CHANGED
@@ -19,14 +19,17 @@ export class MsSqlDriver extends AbstractSqlDriver {
19
19
  const hasFields = fields.length > 0;
20
20
  // Is this en empty insert... this is rather hard in mssql (especially with an insert many)
21
21
  if (!hasFields) {
22
- const returningProps = meta.props.filter(prop => prop.primary || prop.defaultRaw);
22
+ const returningProps = this.getTableProps(meta).filter(prop => prop.returning || prop.primary || prop.defaultRaw);
23
23
  const returningFields = Utils.flatten(returningProps.map(prop => prop.fieldNames));
24
24
  const using2 = `select * from (values ${data.map((x, i) => `(${i})`).join(',')}) v (id) where 1 = 1`;
25
25
  /* v8 ignore next */
26
26
  const output = returningFields.length > 0
27
27
  ? `output ${returningFields.map(field => 'inserted.' + this.platform.quoteIdentifier(field)).join(', ')}`
28
28
  : '';
29
- const sql = `merge into ${tableName} using (${using2}) s on 1 = 0 when not matched then insert default values ${output};`;
29
+ let sql = `merge into ${tableName} using (${using2}) s on 1 = 0 when not matched then insert default values ${output};`;
30
+ if (meta.hasTriggers) {
31
+ sql = this.appendOutputTable(entityName, returningFields, sql, sql.length - 1, options);
32
+ }
30
33
  const res = await this.execute(sql, [], 'run', options.ctx);
31
34
  const pks = this.getPrimaryKeyFields(meta);
32
35
  if (pks.length === 1) {
@@ -36,13 +39,20 @@ export class MsSqlDriver extends AbstractSqlDriver {
36
39
  }
37
40
  return res;
38
41
  }
39
- // For TPT child entities, the parent table owns the identity column, not the child table
40
- if (props.some(prop => prop.autoincrement && (!meta.ownProps || meta.ownProps.includes(prop)))) {
41
- return super.nativeInsertMany(entityName, data, options, sql => {
42
- return `set identity_insert ${tableName} on; ${sql}; set identity_insert ${tableName} off`;
43
- });
44
- }
45
- return super.nativeInsertMany(entityName, data, options, sql => meta.hasTriggers ? this.appendOutputTable(entityName, data, sql) : sql);
42
+ return super.nativeInsertMany(entityName, data, options, sql => {
43
+ if (meta.hasTriggers) {
44
+ // must match the OUTPUT columns of the parent implementation, which resolves STI children to the root
45
+ const returning = this.getTableProps(meta.inheritanceType === 'tpt' ? meta : meta.root).filter(prop => prop.returning ||
46
+ (((prop.persist !== false && prop.defaultRaw) || prop.autoincrement || prop.generated) &&
47
+ (!(prop.name in data[0]) || isRaw(data[0][prop.name]))));
48
+ sql = this.appendOutputTable(entityName, returning.flatMap(p => p.fieldNames), sql, sql.indexOf(' values '), options);
49
+ }
50
+ // For TPT children, the parent table owns the identity column.
51
+ if (props.some(prop => prop.autoincrement && (!meta.ownProps || meta.ownProps.includes(prop)))) {
52
+ sql = `set identity_insert ${tableName} on; ${sql}; set identity_insert ${tableName} off`;
53
+ }
54
+ return sql;
55
+ });
46
56
  }
47
57
  createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes, loggerContext, alias, em) {
48
58
  // do not compute the connectionType if EM is provided as it will be computed from it in the QB later on
@@ -55,24 +65,19 @@ export class MsSqlDriver extends AbstractSqlDriver {
55
65
  }
56
66
  return qb;
57
67
  }
58
- appendOutputTable(entityName, data, sql) {
68
+ appendOutputTable(entityName, returningFields, sql, position, options) {
59
69
  const meta = this.metadata.get(entityName);
60
- const returningProps = meta.props
61
- .filter(prop => (prop.persist !== false && prop.defaultRaw) || prop.autoincrement || prop.generated)
62
- .filter(prop => !(prop.name in data[0]) || isRaw(data[0][prop.name]));
63
- const returningFields = Utils.flatten(returningProps.map(prop => prop.fieldNames));
64
70
  /* v8 ignore next */
65
71
  if (returningFields.length === 0) {
66
72
  return sql;
67
73
  }
68
- const tableName = this.getTableName(meta, {}, true);
74
+ const tableName = this.getTableName(meta, options, true);
69
75
  const selections = returningFields.map((field) => `[t].${this.platform.quoteIdentifier(field)}`).join(',');
70
- const position = sql.indexOf(' values ');
71
76
  const sqlBeforeValues = sql.substring(0, position);
72
- const sqlAfterValues = sql.substring(position + 1);
77
+ const sqlAfterValues = sql.substring(position);
73
78
  let outputSql = `select top(0) ${selections} into #out from ${tableName} as t left join ${tableName} on 0 = 1; `;
74
- outputSql += `${sqlBeforeValues} into #out ${sqlAfterValues}; `;
75
- outputSql += `select ${selections} from #out as t; `;
79
+ outputSql += `${sqlBeforeValues} into #out${sqlAfterValues}; `;
80
+ outputSql += `select ${selections} from #out as t; select @@rowcount as [__mikro_orm_row_count__]; `;
76
81
  outputSql += `drop table #out`;
77
82
  return outputSql;
78
83
  }
@@ -1,7 +1,8 @@
1
- import { type AnyEntity, type RequiredEntityData } from '@mikro-orm/core';
2
- import { type InsertQueryBuilder, QueryBuilder } from '@mikro-orm/sql';
1
+ import { type AnyEntity, type EntityData, type RequiredEntityData } from '@mikro-orm/core';
2
+ import { type InsertQueryBuilder, type UpdateQueryBuilder, QueryBuilder } from '@mikro-orm/sql';
3
3
  /** Query builder with MSSQL-specific behavior such as identity insert handling. */
4
4
  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> {
5
5
  insert(data: RequiredEntityData<Entity> | RequiredEntityData<Entity>[]): InsertQueryBuilder<Entity, RootAlias, Context>;
6
+ update(data: EntityData<Entity>): UpdateQueryBuilder<Entity, RootAlias, Context>;
6
7
  private checkIdentityInsert;
7
8
  }
@@ -4,18 +4,24 @@ import { QueryBuilder } from '@mikro-orm/sql';
4
4
  export class MsSqlQueryBuilder extends QueryBuilder {
5
5
  insert(data) {
6
6
  this.checkIdentityInsert(data);
7
- if (!this.hasFlag(QueryFlag.IDENTITY_INSERT) && this.metadata.has(this.mainAlias.entityName)) {
8
- const meta = this.mainAlias.meta;
9
- if (meta.hasTriggers) {
10
- this.setFlag(QueryFlag.OUTPUT_TABLE);
11
- }
7
+ if (this.mainAlias.meta?.hasTriggers) {
8
+ this.setFlag(QueryFlag.OUTPUT_TABLE);
12
9
  }
13
10
  return super.insert(data);
14
11
  }
12
+ update(data) {
13
+ if (this.mainAlias.meta?.hasTriggers) {
14
+ this.setFlag(QueryFlag.OUTPUT_TABLE);
15
+ }
16
+ return super.update(data);
17
+ }
15
18
  checkIdentityInsert(data) {
16
19
  const meta = this.mainAlias.meta;
17
20
  const dataKeys = Utils.unique(Utils.asArray(data).flatMap(d => Utils.keys(d)));
18
- const hasAutoincrement = dataKeys.some(x => meta.properties[x]?.autoincrement);
21
+ const hasAutoincrement = dataKeys.some(x => {
22
+ const prop = meta.properties[x];
23
+ return prop?.autoincrement && (!meta.ownProps || meta.ownProps.includes(prop));
24
+ });
19
25
  if (hasAutoincrement) {
20
26
  this.setFlag(QueryFlag.IDENTITY_INSERT);
21
27
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/mssql",
3
- "version": "7.2.2-dev.1",
3
+ "version": "7.2.2-dev.3",
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,7 +47,7 @@
47
47
  "copy": "node ../../scripts/copy.mjs"
48
48
  },
49
49
  "dependencies": {
50
- "@mikro-orm/sql": "7.2.2-dev.1",
50
+ "@mikro-orm/sql": "7.2.2-dev.3",
51
51
  "kysely": "0.29.6",
52
52
  "tarn": "3.1.2",
53
53
  "tedious": "20.0.0",
@@ -57,7 +57,7 @@
57
57
  "@mikro-orm/core": "^7.2.1"
58
58
  },
59
59
  "peerDependencies": {
60
- "@mikro-orm/core": "7.2.2-dev.1"
60
+ "@mikro-orm/core": "7.2.2-dev.3"
61
61
  },
62
62
  "engines": {
63
63
  "node": ">= 22.17.0"