@mikro-orm/mssql 7.1.0-dev.43 → 7.1.0-dev.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MsSqlConnection.d.ts +2 -0
- package/MsSqlConnection.js +48 -1
- package/MsSqlSchemaHelper.d.ts +9 -1
- package/MsSqlSchemaHelper.js +107 -0
- package/package.json +3 -3
package/MsSqlConnection.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { AbstractSqlConnection, type TransactionEventBroadcaster } from '@mikro-orm/sql';
|
|
2
2
|
import { type ControlledTransaction, MssqlDialect } from 'kysely';
|
|
3
|
+
import { type Routine, type Transaction } from '@mikro-orm/core';
|
|
3
4
|
import type { ConnectionConfiguration } from 'tedious';
|
|
4
5
|
/** Microsoft SQL Server database connection using the `tedious` driver. */
|
|
5
6
|
export declare class MsSqlConnection extends AbstractSqlConnection {
|
|
6
7
|
createKyselyDialect(overrides: ConnectionConfiguration): MssqlDialect;
|
|
7
8
|
private mapOptions;
|
|
9
|
+
callRoutine<T>(routine: Routine, args?: Record<string, unknown>, ctx?: Transaction): Promise<T>;
|
|
8
10
|
commit(ctx: ControlledTransaction<any, any>, eventBroadcaster?: TransactionEventBroadcaster): Promise<void>;
|
|
9
11
|
protected transformRawResult<T>(res: any, method: 'all' | 'get' | 'run'): T;
|
|
10
12
|
}
|
package/MsSqlConnection.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbstractSqlConnection, Utils } from '@mikro-orm/sql';
|
|
1
|
+
import { AbstractSqlConnection, DatabaseSchema, 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';
|
|
@@ -55,6 +55,53 @@ export class MsSqlConnection extends AbstractSqlConnection {
|
|
|
55
55
|
}
|
|
56
56
|
return Utils.mergeConfig(ret, overrides);
|
|
57
57
|
}
|
|
58
|
+
async callRoutine(routine, args = {}, ctx) {
|
|
59
|
+
if (routine.type === 'function') {
|
|
60
|
+
return this.callRoutineFunction(routine, args, ctx);
|
|
61
|
+
}
|
|
62
|
+
// MSSQL scalar UDF calls must be schema-qualified — `select sql_hash(...)` fails to parse.
|
|
63
|
+
const schema = routine.schema ?? this.platform.getDefaultSchemaName() ?? 'dbo';
|
|
64
|
+
const qualified = `${this.platform.quoteIdentifier(schema)}.${this.platform.quoteIdentifier(routine.name)}`;
|
|
65
|
+
// T-SQL session variables don't persist across execute() calls (different pool connections),
|
|
66
|
+
// so DECLARE/SET/EXEC/SELECT must go through as a single batch.
|
|
67
|
+
const declareLines = [];
|
|
68
|
+
const setLines = [];
|
|
69
|
+
const setValues = [];
|
|
70
|
+
const callArgs = [];
|
|
71
|
+
const inValues = [];
|
|
72
|
+
const outVars = [];
|
|
73
|
+
routine.params.forEach((p, i) => {
|
|
74
|
+
if (p.direction === 'in') {
|
|
75
|
+
callArgs.push('?');
|
|
76
|
+
inValues.push(this.convertRoutineInbound(args[p.name], p));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const varName = `@_mikro_orm_routine_${i}`;
|
|
80
|
+
// Logical aliases like `'string'`/`'number'` aren't valid T-SQL types — translate them
|
|
81
|
+
// through the platform's type system, matching the DDL side in `DatabaseSchema`.
|
|
82
|
+
const declType = DatabaseSchema.resolveRoutineColumnType(p.type, this.platform);
|
|
83
|
+
declareLines.push(`declare ${varName} ${declType}`);
|
|
84
|
+
outVars.push({ name: p.name, varName, param: p });
|
|
85
|
+
if (p.direction === 'inout') {
|
|
86
|
+
setLines.push(`set ${varName} = ?`);
|
|
87
|
+
setValues.push(this.convertRoutineInbound(args[p.name], p));
|
|
88
|
+
}
|
|
89
|
+
callArgs.push(`${varName} output`);
|
|
90
|
+
});
|
|
91
|
+
const allValues = [...setValues, ...inValues];
|
|
92
|
+
const batch = [...declareLines, ...setLines, `exec ${qualified} ${callArgs.join(', ')}`];
|
|
93
|
+
if (outVars.length > 0) {
|
|
94
|
+
const selectClause = outVars.map(o => `${o.varName} as ${this.platform.quoteIdentifier(o.name)}`).join(', ');
|
|
95
|
+
batch.push(`select ${selectClause}`);
|
|
96
|
+
}
|
|
97
|
+
const result = await this.execute(batch.join('; '), allValues, 'all', ctx);
|
|
98
|
+
if (outVars.length === 0) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
const rows = result;
|
|
102
|
+
this.applyRoutineOutParams(rows[0] ?? {}, outVars.map(o => o.param), args);
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
58
105
|
async commit(ctx, eventBroadcaster) {
|
|
59
106
|
if ('savepointName' in ctx) {
|
|
60
107
|
return;
|
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 SqlTriggerDef, 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 SqlRoutineDef, 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: {
|
|
@@ -29,6 +29,14 @@ export declare class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
29
29
|
createTrigger(table: DatabaseTable, trigger: SqlTriggerDef): string;
|
|
30
30
|
/** Generates SQL to drop an MSSQL trigger. */
|
|
31
31
|
dropTrigger(table: DatabaseTable, trigger: SqlTriggerDef): string;
|
|
32
|
+
routineParamReference(name: string): string;
|
|
33
|
+
/** T-SQL's `OUTPUT` covers both OUT and INOUT; `sys.parameters.is_output` is true for both. */
|
|
34
|
+
normaliseRoutineParamDirection(direction: 'in' | 'out' | 'inout'): 'in' | 'out' | 'inout';
|
|
35
|
+
createRoutine(routine: SqlRoutineDef): string;
|
|
36
|
+
dropRoutine(routine: SqlRoutineDef): string;
|
|
37
|
+
getAllRoutines(connection: AbstractSqlConnection): Promise<SqlRoutineDef[]>;
|
|
38
|
+
private getAllRoutineParams;
|
|
39
|
+
private unwrapMsSqlBody;
|
|
32
40
|
private getSchemaQualifiedName;
|
|
33
41
|
getDatabaseCollation(connection: AbstractSqlConnection, ctx?: Transaction): Promise<string | undefined>;
|
|
34
42
|
getAllTriggers(connection: AbstractSqlConnection, tablesBySchemas: Map<string | undefined, Table[]>): Promise<Dictionary<SqlTriggerDef[]>>;
|
package/MsSqlSchemaHelper.js
CHANGED
|
@@ -353,6 +353,113 @@ export class MsSqlSchemaHelper extends SchemaHelper {
|
|
|
353
353
|
dropTrigger(table, trigger) {
|
|
354
354
|
return `drop trigger if exists ${this.getSchemaQualifiedName(table, trigger.name)}`;
|
|
355
355
|
}
|
|
356
|
+
routineParamReference(name) {
|
|
357
|
+
return `@${name}`;
|
|
358
|
+
}
|
|
359
|
+
/** T-SQL's `OUTPUT` covers both OUT and INOUT; `sys.parameters.is_output` is true for both. */
|
|
360
|
+
normaliseRoutineParamDirection(direction) {
|
|
361
|
+
return direction === 'out' ? 'inout' : direction;
|
|
362
|
+
}
|
|
363
|
+
createRoutine(routine) {
|
|
364
|
+
if (routine.expression) {
|
|
365
|
+
return routine.expression;
|
|
366
|
+
}
|
|
367
|
+
const qualifiedName = this.qualifiedRoutineName(routine);
|
|
368
|
+
const params = routine.params
|
|
369
|
+
.map(p => {
|
|
370
|
+
const dir = p.direction === 'out' || p.direction === 'inout' ? ' OUTPUT' : '';
|
|
371
|
+
return `@${p.name} ${p.type}${dir}`;
|
|
372
|
+
})
|
|
373
|
+
.join(', ');
|
|
374
|
+
const body = this.wrapRoutineBody(routine.body ?? '');
|
|
375
|
+
if (routine.type === 'procedure') {
|
|
376
|
+
return `create or alter procedure ${qualifiedName} ${params} as ${body}`;
|
|
377
|
+
}
|
|
378
|
+
const returnType = routine.returns?.type ?? 'nvarchar(max)';
|
|
379
|
+
return `create or alter function ${qualifiedName}(${params}) returns ${returnType} as ${body}`;
|
|
380
|
+
}
|
|
381
|
+
dropRoutine(routine) {
|
|
382
|
+
const kind = routine.type === 'procedure' ? 'procedure' : 'function';
|
|
383
|
+
return `drop ${kind} if exists ${this.qualifiedRoutineName(routine)}`;
|
|
384
|
+
}
|
|
385
|
+
async getAllRoutines(connection) {
|
|
386
|
+
const sql = `
|
|
387
|
+
select
|
|
388
|
+
s.name as schema_name,
|
|
389
|
+
o.name as name,
|
|
390
|
+
case
|
|
391
|
+
when o.type = 'P' then 'procedure'
|
|
392
|
+
when o.type in ('FN', 'IF', 'TF') then 'function'
|
|
393
|
+
end as kind,
|
|
394
|
+
m.definition as definition,
|
|
395
|
+
ep.value as comment
|
|
396
|
+
from sys.objects o
|
|
397
|
+
join sys.schemas s on s.schema_id = o.schema_id
|
|
398
|
+
join sys.sql_modules m on m.object_id = o.object_id
|
|
399
|
+
left join sys.extended_properties ep
|
|
400
|
+
on ep.major_id = o.object_id and ep.minor_id = 0 and ep.name = 'MS_Description'
|
|
401
|
+
where o.type in ('P', 'FN', 'IF', 'TF')
|
|
402
|
+
and o.is_ms_shipped = 0
|
|
403
|
+
`;
|
|
404
|
+
const [rows, paramsAndReturns] = await Promise.all([
|
|
405
|
+
connection.execute(sql),
|
|
406
|
+
this.getAllRoutineParams(connection),
|
|
407
|
+
]);
|
|
408
|
+
const { params, returns } = paramsAndReturns;
|
|
409
|
+
return rows.map(row => ({
|
|
410
|
+
name: row.name,
|
|
411
|
+
schema: row.schema_name,
|
|
412
|
+
type: row.kind,
|
|
413
|
+
body: this.unwrapMsSqlBody(row.definition),
|
|
414
|
+
comment: row.comment ?? undefined,
|
|
415
|
+
params: params.get(`${row.schema_name}.${row.name}`) ?? [],
|
|
416
|
+
returns: row.kind === 'function'
|
|
417
|
+
? (returns.get(`${row.schema_name}.${row.name}`) ?? { type: 'nvarchar(max)', nullable: true })
|
|
418
|
+
: undefined,
|
|
419
|
+
}));
|
|
420
|
+
}
|
|
421
|
+
async getAllRoutineParams(connection) {
|
|
422
|
+
// `parameter_id = 0` is the function's return type; positive IDs are formal parameters.
|
|
423
|
+
const sql = `
|
|
424
|
+
select
|
|
425
|
+
s.name as schema_name,
|
|
426
|
+
o.name as routine_name,
|
|
427
|
+
p.name as param_name,
|
|
428
|
+
type_name(p.user_type_id) as type,
|
|
429
|
+
p.is_output as is_output,
|
|
430
|
+
p.parameter_id as position
|
|
431
|
+
from sys.parameters p
|
|
432
|
+
join sys.objects o on o.object_id = p.object_id
|
|
433
|
+
join sys.schemas s on s.schema_id = o.schema_id
|
|
434
|
+
where o.type in ('P', 'FN', 'IF', 'TF')
|
|
435
|
+
and o.is_ms_shipped = 0
|
|
436
|
+
order by o.object_id, p.parameter_id
|
|
437
|
+
`;
|
|
438
|
+
const rows = await connection.execute(sql);
|
|
439
|
+
const params = new Map();
|
|
440
|
+
const returns = new Map();
|
|
441
|
+
for (const row of rows) {
|
|
442
|
+
const key = `${row.schema_name}.${row.routine_name}`;
|
|
443
|
+
if (row.position === 0) {
|
|
444
|
+
returns.set(key, { type: row.type, nullable: true });
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
if (!params.has(key)) {
|
|
448
|
+
params.set(key, []);
|
|
449
|
+
}
|
|
450
|
+
// is_output is true for both OUT and INOUT; we always report `inout`. See normaliseRoutineParamDirection.
|
|
451
|
+
params.get(key).push({
|
|
452
|
+
name: row.param_name.replace(/^@/, ''),
|
|
453
|
+
type: row.type,
|
|
454
|
+
direction: row.is_output ? 'inout' : 'in',
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
return { params, returns };
|
|
458
|
+
}
|
|
459
|
+
unwrapMsSqlBody(definition) {
|
|
460
|
+
const asMatch = /\bas\s+([\s\S]*)$/i.exec(definition);
|
|
461
|
+
return this.stripRoutineBody(asMatch ? asMatch[1] : definition);
|
|
462
|
+
}
|
|
356
463
|
getSchemaQualifiedName(table, name) {
|
|
357
464
|
const defaultSchema = this.platform.getDefaultSchemaName();
|
|
358
465
|
if (table.schema && table.schema !== defaultSchema) {
|
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.45",
|
|
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.1.0-dev.
|
|
50
|
+
"@mikro-orm/sql": "7.1.0-dev.45",
|
|
51
51
|
"kysely": "0.29.2",
|
|
52
52
|
"tarn": "3.0.2",
|
|
53
53
|
"tedious": "19.2.1",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"@mikro-orm/core": "^7.0.17"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
|
-
"@mikro-orm/core": "7.1.0-dev.
|
|
60
|
+
"@mikro-orm/core": "7.1.0-dev.45"
|
|
61
61
|
},
|
|
62
62
|
"engines": {
|
|
63
63
|
"node": ">= 22.17.0"
|