@mikro-orm/mssql 7.2.2-dev.2 → 7.2.2-dev.21
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.js +11 -8
- package/MsSqlDriver.d.ts +2 -1
- package/MsSqlDriver.js +34 -19
- package/MsSqlQueryBuilder.d.ts +4 -2
- package/MsSqlQueryBuilder.js +40 -7
- package/package.json +3 -3
package/MsSqlConnection.js
CHANGED
|
@@ -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
|
|
150
|
+
return outputRows[0];
|
|
148
151
|
}
|
|
149
152
|
if (method === 'all') {
|
|
150
|
-
return
|
|
153
|
+
return outputRows;
|
|
151
154
|
}
|
|
152
|
-
const
|
|
153
|
-
const
|
|
154
|
-
const
|
|
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:
|
|
157
|
-
row:
|
|
158
|
-
rows
|
|
159
|
+
affectedRows: Number(count),
|
|
160
|
+
row: rows[0],
|
|
161
|
+
rows,
|
|
159
162
|
};
|
|
160
163
|
}
|
|
161
164
|
}
|
package/MsSqlDriver.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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';
|
|
1
|
+
import { type AnyEntity, type Configuration, type ConnectionType, type EntityDictionary, type EntityName, type LoggingOptions, type NativeInsertUpdateManyOptions, type QueryResult, type Transaction, type Constructor, type FilterQuery, type UpsertManyOptions } from '@mikro-orm/core';
|
|
2
2
|
import { AbstractSqlDriver, type SqlEntityManager } from '@mikro-orm/sql';
|
|
3
3
|
import { MsSqlConnection } from './MsSqlConnection.js';
|
|
4
4
|
import { MsSqlQueryBuilder } from './MsSqlQueryBuilder.js';
|
|
@@ -7,6 +7,7 @@ import { MsSqlMikroORM } from './MsSqlMikroORM.js';
|
|
|
7
7
|
export declare class MsSqlDriver extends AbstractSqlDriver<MsSqlConnection> {
|
|
8
8
|
constructor(config: Configuration);
|
|
9
9
|
nativeInsertMany<T extends AnyEntity<T>>(entityName: EntityName<T>, data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T>): Promise<QueryResult<T>>;
|
|
10
|
+
nativeUpdateMany<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>[], data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T> & UpsertManyOptions<T>): Promise<QueryResult<T>>;
|
|
10
11
|
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>;
|
|
11
12
|
private appendOutputTable;
|
|
12
13
|
/** @inheritDoc */
|
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.
|
|
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
|
-
|
|
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,30 @@ export class MsSqlDriver extends AbstractSqlDriver {
|
|
|
36
39
|
}
|
|
37
40
|
return res;
|
|
38
41
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async nativeUpdateMany(entityName, where, data, options = {}) {
|
|
58
|
+
const meta = this.metadata.get(entityName);
|
|
59
|
+
return super.nativeUpdateMany(entityName, where, data, options, sql => {
|
|
60
|
+
if (!meta.hasTriggers) {
|
|
61
|
+
return sql;
|
|
62
|
+
}
|
|
63
|
+
const fields = this.getUpdateReturningProperties(meta, data).flatMap(prop => prop.fieldNames);
|
|
64
|
+
return this.appendOutputTable(entityName, fields, sql, sql.lastIndexOf(' where '), options);
|
|
65
|
+
});
|
|
46
66
|
}
|
|
47
67
|
createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes, loggerContext, alias, em) {
|
|
48
68
|
// do not compute the connectionType if EM is provided as it will be computed from it in the QB later on
|
|
@@ -55,24 +75,19 @@ export class MsSqlDriver extends AbstractSqlDriver {
|
|
|
55
75
|
}
|
|
56
76
|
return qb;
|
|
57
77
|
}
|
|
58
|
-
appendOutputTable(entityName,
|
|
78
|
+
appendOutputTable(entityName, returningFields, sql, position, options) {
|
|
59
79
|
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
80
|
/* v8 ignore next */
|
|
65
81
|
if (returningFields.length === 0) {
|
|
66
82
|
return sql;
|
|
67
83
|
}
|
|
68
|
-
const tableName = this.getTableName(meta,
|
|
84
|
+
const tableName = this.getTableName(meta, options, true);
|
|
69
85
|
const selections = returningFields.map((field) => `[t].${this.platform.quoteIdentifier(field)}`).join(',');
|
|
70
|
-
const position = sql.indexOf(' values ');
|
|
71
86
|
const sqlBeforeValues = sql.substring(0, position);
|
|
72
|
-
const sqlAfterValues = sql.substring(position
|
|
87
|
+
const sqlAfterValues = sql.substring(position);
|
|
73
88
|
let outputSql = `select top(0) ${selections} into #out from ${tableName} as t left join ${tableName} on 0 = 1; `;
|
|
74
|
-
outputSql += `${sqlBeforeValues} into #out
|
|
75
|
-
outputSql += `select ${selections} from #out as t; `;
|
|
89
|
+
outputSql += `${sqlBeforeValues} into #out${sqlAfterValues}; `;
|
|
90
|
+
outputSql += `select ${selections} from #out as t; select @@rowcount as [__mikro_orm_row_count__]; `;
|
|
76
91
|
outputSql += `drop table #out`;
|
|
77
92
|
return outputSql;
|
|
78
93
|
}
|
package/MsSqlQueryBuilder.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
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>;
|
|
7
|
+
private applyInsertDefaults;
|
|
6
8
|
private checkIdentityInsert;
|
|
7
9
|
}
|
package/MsSqlQueryBuilder.js
CHANGED
|
@@ -1,21 +1,54 @@
|
|
|
1
|
-
import { QueryFlag, Utils } from '@mikro-orm/core';
|
|
1
|
+
import { QueryFlag, Utils, raw } from '@mikro-orm/core';
|
|
2
2
|
import { QueryBuilder } from '@mikro-orm/sql';
|
|
3
3
|
/** Query builder with MSSQL-specific behavior such as identity insert handling. */
|
|
4
4
|
export class MsSqlQueryBuilder extends QueryBuilder {
|
|
5
5
|
insert(data) {
|
|
6
|
+
data = this.applyInsertDefaults(data);
|
|
6
7
|
this.checkIdentityInsert(data);
|
|
7
|
-
if (
|
|
8
|
-
|
|
9
|
-
if (meta.hasTriggers) {
|
|
10
|
-
this.setFlag(QueryFlag.OUTPUT_TABLE);
|
|
11
|
-
}
|
|
8
|
+
if (this.mainAlias.meta?.hasTriggers) {
|
|
9
|
+
this.setFlag(QueryFlag.OUTPUT_TABLE);
|
|
12
10
|
}
|
|
13
11
|
return super.insert(data);
|
|
14
12
|
}
|
|
13
|
+
update(data) {
|
|
14
|
+
if (this.mainAlias.meta?.hasTriggers) {
|
|
15
|
+
this.setFlag(QueryFlag.OUTPUT_TABLE);
|
|
16
|
+
}
|
|
17
|
+
return super.update(data);
|
|
18
|
+
}
|
|
19
|
+
applyInsertDefaults(data) {
|
|
20
|
+
if (!Array.isArray(data) || data.length < 2) {
|
|
21
|
+
return data;
|
|
22
|
+
}
|
|
23
|
+
const meta = this.mainAlias.meta;
|
|
24
|
+
const keys = Utils.unique(data.flatMap(row => Utils.keys(row))).filter(key => meta.properties[key]?.defaultRaw);
|
|
25
|
+
if (keys.length === 0) {
|
|
26
|
+
return data;
|
|
27
|
+
}
|
|
28
|
+
const schemaHelper = this.platform.getSchemaHelper();
|
|
29
|
+
// MERGE uses a derived VALUES table, where DEFAULT is not allowed.
|
|
30
|
+
return data.map(row => {
|
|
31
|
+
let copy = row;
|
|
32
|
+
for (const key of keys) {
|
|
33
|
+
if (row[key] === undefined) {
|
|
34
|
+
if (copy === row) {
|
|
35
|
+
copy = { ...row };
|
|
36
|
+
}
|
|
37
|
+
const prop = meta.properties[key];
|
|
38
|
+
// same normalization as the DDL, e.g. `true` is not a valid MSSQL literal
|
|
39
|
+
copy[key] = raw(`${schemaHelper.normalizeDefaultValue(prop.defaultRaw, prop.length)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return copy;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
15
45
|
checkIdentityInsert(data) {
|
|
16
46
|
const meta = this.mainAlias.meta;
|
|
17
47
|
const dataKeys = Utils.unique(Utils.asArray(data).flatMap(d => Utils.keys(d)));
|
|
18
|
-
const hasAutoincrement = dataKeys.some(x =>
|
|
48
|
+
const hasAutoincrement = dataKeys.some(x => {
|
|
49
|
+
const prop = meta.properties[x];
|
|
50
|
+
return prop?.autoincrement && (!meta.ownProps || meta.ownProps.includes(prop));
|
|
51
|
+
});
|
|
19
52
|
if (hasAutoincrement) {
|
|
20
53
|
this.setFlag(QueryFlag.IDENTITY_INSERT);
|
|
21
54
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/mssql",
|
|
3
|
-
"version": "7.2.2-dev.
|
|
3
|
+
"version": "7.2.2-dev.21",
|
|
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.
|
|
50
|
+
"@mikro-orm/sql": "7.2.2-dev.21",
|
|
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.
|
|
60
|
+
"@mikro-orm/core": "7.2.2-dev.21"
|
|
61
61
|
},
|
|
62
62
|
"engines": {
|
|
63
63
|
"node": ">= 22.17.0"
|