@mikro-orm/sql 7.0.2-dev.9 → 7.0.2

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.
Files changed (87) hide show
  1. package/AbstractSqlConnection.d.ts +95 -47
  2. package/AbstractSqlConnection.js +240 -232
  3. package/AbstractSqlDriver.d.ts +412 -155
  4. package/AbstractSqlDriver.js +2062 -1937
  5. package/AbstractSqlPlatform.d.ts +84 -73
  6. package/AbstractSqlPlatform.js +163 -158
  7. package/PivotCollectionPersister.d.ts +33 -15
  8. package/PivotCollectionPersister.js +158 -160
  9. package/README.md +128 -294
  10. package/SqlEntityManager.d.ts +68 -20
  11. package/SqlEntityManager.js +54 -37
  12. package/SqlEntityRepository.d.ts +15 -14
  13. package/SqlEntityRepository.js +24 -23
  14. package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +12 -12
  15. package/dialects/mssql/MsSqlNativeQueryBuilder.js +192 -194
  16. package/dialects/mysql/BaseMySqlPlatform.d.ts +64 -45
  17. package/dialects/mysql/BaseMySqlPlatform.js +134 -131
  18. package/dialects/mysql/MySqlExceptionConverter.d.ts +6 -6
  19. package/dialects/mysql/MySqlExceptionConverter.js +91 -77
  20. package/dialects/mysql/MySqlNativeQueryBuilder.d.ts +3 -3
  21. package/dialects/mysql/MySqlNativeQueryBuilder.js +66 -69
  22. package/dialects/mysql/MySqlSchemaHelper.d.ts +39 -39
  23. package/dialects/mysql/MySqlSchemaHelper.js +327 -319
  24. package/dialects/oracledb/OracleDialect.d.ts +81 -52
  25. package/dialects/oracledb/OracleDialect.js +155 -149
  26. package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +12 -12
  27. package/dialects/oracledb/OracleNativeQueryBuilder.js +232 -236
  28. package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +108 -105
  29. package/dialects/postgresql/BasePostgreSqlPlatform.js +351 -350
  30. package/dialects/postgresql/FullTextType.d.ts +10 -6
  31. package/dialects/postgresql/FullTextType.js +51 -51
  32. package/dialects/postgresql/PostgreSqlExceptionConverter.d.ts +5 -5
  33. package/dialects/postgresql/PostgreSqlExceptionConverter.js +55 -43
  34. package/dialects/postgresql/PostgreSqlNativeQueryBuilder.d.ts +1 -1
  35. package/dialects/postgresql/PostgreSqlNativeQueryBuilder.js +4 -4
  36. package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +102 -82
  37. package/dialects/postgresql/PostgreSqlSchemaHelper.js +711 -683
  38. package/dialects/sqlite/BaseSqliteConnection.d.ts +3 -5
  39. package/dialects/sqlite/BaseSqliteConnection.js +21 -19
  40. package/dialects/sqlite/NodeSqliteDialect.d.ts +1 -1
  41. package/dialects/sqlite/NodeSqliteDialect.js +23 -23
  42. package/dialects/sqlite/SqliteDriver.d.ts +1 -1
  43. package/dialects/sqlite/SqliteDriver.js +3 -3
  44. package/dialects/sqlite/SqliteExceptionConverter.d.ts +6 -6
  45. package/dialects/sqlite/SqliteExceptionConverter.js +67 -51
  46. package/dialects/sqlite/SqliteNativeQueryBuilder.d.ts +2 -2
  47. package/dialects/sqlite/SqliteNativeQueryBuilder.js +7 -7
  48. package/dialects/sqlite/SqlitePlatform.d.ts +63 -72
  49. package/dialects/sqlite/SqlitePlatform.js +139 -139
  50. package/dialects/sqlite/SqliteSchemaHelper.d.ts +70 -60
  51. package/dialects/sqlite/SqliteSchemaHelper.js +533 -520
  52. package/package.json +4 -4
  53. package/plugin/index.d.ts +44 -35
  54. package/plugin/index.js +44 -36
  55. package/plugin/transformer.d.ts +117 -94
  56. package/plugin/transformer.js +890 -881
  57. package/query/ArrayCriteriaNode.d.ts +4 -4
  58. package/query/ArrayCriteriaNode.js +18 -18
  59. package/query/CriteriaNode.d.ts +35 -25
  60. package/query/CriteriaNode.js +133 -123
  61. package/query/CriteriaNodeFactory.d.ts +49 -6
  62. package/query/CriteriaNodeFactory.js +97 -94
  63. package/query/NativeQueryBuilder.d.ts +120 -117
  64. package/query/NativeQueryBuilder.js +484 -480
  65. package/query/ObjectCriteriaNode.d.ts +12 -12
  66. package/query/ObjectCriteriaNode.js +298 -282
  67. package/query/QueryBuilder.d.ts +1546 -904
  68. package/query/QueryBuilder.js +2270 -2145
  69. package/query/QueryBuilderHelper.d.ts +153 -72
  70. package/query/QueryBuilderHelper.js +1079 -1028
  71. package/query/ScalarCriteriaNode.d.ts +3 -3
  72. package/query/ScalarCriteriaNode.js +53 -46
  73. package/query/enums.d.ts +16 -14
  74. package/query/enums.js +16 -14
  75. package/query/raw.d.ts +16 -6
  76. package/query/raw.js +10 -10
  77. package/schema/DatabaseSchema.d.ts +73 -50
  78. package/schema/DatabaseSchema.js +331 -307
  79. package/schema/DatabaseTable.d.ts +96 -73
  80. package/schema/DatabaseTable.js +1012 -927
  81. package/schema/SchemaComparator.d.ts +58 -54
  82. package/schema/SchemaComparator.js +745 -719
  83. package/schema/SchemaHelper.d.ts +110 -80
  84. package/schema/SchemaHelper.js +676 -645
  85. package/schema/SqlSchemaGenerator.d.ts +79 -58
  86. package/schema/SqlSchemaGenerator.js +536 -501
  87. package/typings.d.ts +380 -266
@@ -1,44 +1,61 @@
1
- import { EntityManager, } from '@mikro-orm/core';
1
+ import { EntityManager } from '@mikro-orm/core';
2
2
  import { MikroKyselyPlugin } from './plugin/index.js';
3
3
  /**
4
4
  * @inheritDoc
5
5
  */
6
6
  export class SqlEntityManager extends EntityManager {
7
- /**
8
- * Creates a QueryBuilder instance
9
- */
10
- createQueryBuilder(entityName, alias, type, loggerContext) {
11
- const context = this.getContext(false);
12
- return this.driver.createQueryBuilder(entityName, context.getTransactionContext(), type, true, loggerContext ?? context.loggerContext, alias, this);
13
- }
14
- /**
15
- * Shortcut for `createQueryBuilder()`
16
- */
17
- qb(entityName, alias, type, loggerContext) {
18
- return this.createQueryBuilder(entityName, alias, type, loggerContext);
19
- }
20
- /**
21
- * Returns configured Kysely instance.
22
- */
23
- getKysely(options = {}) {
24
- let kysely = this.getConnection(options.type).getClient();
25
- if (options.columnNamingStrategy != null ||
26
- options.tableNamingStrategy != null ||
27
- options.processOnCreateHooks != null ||
28
- options.processOnUpdateHooks != null ||
29
- options.convertValues != null) {
30
- kysely = kysely.withPlugin(new MikroKyselyPlugin(this, options));
31
- }
32
- return kysely;
33
- }
34
- async execute(query, params = [], method = 'all', loggerContext) {
35
- return this.getDriver().execute(query, params, method, this.getContext(false).getTransactionContext(), loggerContext);
36
- }
37
- getRepository(entityName) {
38
- return super.getRepository(entityName);
39
- }
40
- applyDiscriminatorCondition(entityName, where) {
41
- // this is handled in QueryBuilder now for SQL drivers
42
- return where;
7
+ /**
8
+ * Creates a QueryBuilder instance
9
+ */
10
+ createQueryBuilder(entityName, alias, type, loggerContext) {
11
+ const context = this.getContext(false);
12
+ return this.driver.createQueryBuilder(
13
+ entityName,
14
+ context.getTransactionContext(),
15
+ type,
16
+ true,
17
+ loggerContext ?? context.loggerContext,
18
+ alias,
19
+ this,
20
+ );
21
+ }
22
+ /**
23
+ * Shortcut for `createQueryBuilder()`
24
+ */
25
+ qb(entityName, alias, type, loggerContext) {
26
+ return this.createQueryBuilder(entityName, alias, type, loggerContext);
27
+ }
28
+ /**
29
+ * Returns configured Kysely instance.
30
+ */
31
+ getKysely(options = {}) {
32
+ let kysely = this.getConnection(options.type).getClient();
33
+ if (
34
+ options.columnNamingStrategy != null ||
35
+ options.tableNamingStrategy != null ||
36
+ options.processOnCreateHooks != null ||
37
+ options.processOnUpdateHooks != null ||
38
+ options.convertValues != null
39
+ ) {
40
+ kysely = kysely.withPlugin(new MikroKyselyPlugin(this, options));
43
41
  }
42
+ return kysely;
43
+ }
44
+ /** Executes a raw SQL query, using the current transaction context if available. */
45
+ async execute(query, params = [], method = 'all', loggerContext) {
46
+ return this.getDriver().execute(
47
+ query,
48
+ params,
49
+ method,
50
+ this.getContext(false).getTransactionContext(),
51
+ loggerContext,
52
+ );
53
+ }
54
+ getRepository(entityName) {
55
+ return super.getRepository(entityName);
56
+ }
57
+ applyDiscriminatorCondition(entityName, where) {
58
+ // this is handled in QueryBuilder now for SQL drivers
59
+ return where;
60
+ }
44
61
  }
@@ -1,19 +1,20 @@
1
1
  import { EntityRepository, type EntityName } from '@mikro-orm/core';
2
2
  import type { SqlEntityManager } from './SqlEntityManager.js';
3
3
  import type { QueryBuilder } from './query/QueryBuilder.js';
4
+ /** SQL-specific entity repository with QueryBuilder support. */
4
5
  export declare class SqlEntityRepository<Entity extends object> extends EntityRepository<Entity> {
5
- protected readonly em: SqlEntityManager;
6
- constructor(em: SqlEntityManager, entityName: EntityName<Entity>);
7
- /**
8
- * Creates a QueryBuilder instance
9
- */
10
- createQueryBuilder<RootAlias extends string = never>(alias?: RootAlias): QueryBuilder<Entity, RootAlias>;
11
- /**
12
- * Shortcut for `createQueryBuilder()`
13
- */
14
- qb<RootAlias extends string = never>(alias?: RootAlias): QueryBuilder<Entity, RootAlias>;
15
- /**
16
- * @inheritDoc
17
- */
18
- getEntityManager(): SqlEntityManager;
6
+ protected readonly em: SqlEntityManager;
7
+ constructor(em: SqlEntityManager, entityName: EntityName<Entity>);
8
+ /**
9
+ * Creates a QueryBuilder instance
10
+ */
11
+ createQueryBuilder<RootAlias extends string = never>(alias?: RootAlias): QueryBuilder<Entity, RootAlias>;
12
+ /**
13
+ * Shortcut for `createQueryBuilder()`
14
+ */
15
+ qb<RootAlias extends string = never>(alias?: RootAlias): QueryBuilder<Entity, RootAlias>;
16
+ /**
17
+ * @inheritDoc
18
+ */
19
+ getEntityManager(): SqlEntityManager;
19
20
  }
@@ -1,26 +1,27 @@
1
1
  import { EntityRepository } from '@mikro-orm/core';
2
+ /** SQL-specific entity repository with QueryBuilder support. */
2
3
  export class SqlEntityRepository extends EntityRepository {
3
- em;
4
- constructor(em, entityName) {
5
- super(em, entityName);
6
- this.em = em;
7
- }
8
- /**
9
- * Creates a QueryBuilder instance
10
- */
11
- createQueryBuilder(alias) {
12
- return this.getEntityManager().createQueryBuilder(this.entityName, alias);
13
- }
14
- /**
15
- * Shortcut for `createQueryBuilder()`
16
- */
17
- qb(alias) {
18
- return this.createQueryBuilder(alias);
19
- }
20
- /**
21
- * @inheritDoc
22
- */
23
- getEntityManager() {
24
- return this.em;
25
- }
4
+ em;
5
+ constructor(em, entityName) {
6
+ super(em, entityName);
7
+ this.em = em;
8
+ }
9
+ /**
10
+ * Creates a QueryBuilder instance
11
+ */
12
+ createQueryBuilder(alias) {
13
+ return this.getEntityManager().createQueryBuilder(this.entityName, alias);
14
+ }
15
+ /**
16
+ * Shortcut for `createQueryBuilder()`
17
+ */
18
+ qb(alias) {
19
+ return this.createQueryBuilder(alias);
20
+ }
21
+ /**
22
+ * @inheritDoc
23
+ */
24
+ getEntityManager() {
25
+ return this.em;
26
+ }
26
27
  }
@@ -1,16 +1,16 @@
1
1
  import { NativeQueryBuilder } from '../../query/NativeQueryBuilder.js';
2
2
  /** @internal */
3
3
  export declare class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
4
- compile(): {
5
- sql: string;
6
- params: unknown[];
7
- };
8
- protected compileInsert(): void;
9
- private appendOutputTable;
10
- private compileUpsert;
11
- protected compileSelect(): void;
12
- protected addLockClause(): void;
13
- protected compileTruncate(): void;
14
- /** MSSQL has no RECURSIVE keyword — CTEs are implicitly recursive. */
15
- protected getCteKeyword(_hasRecursive: boolean): string;
4
+ compile(): {
5
+ sql: string;
6
+ params: unknown[];
7
+ };
8
+ protected compileInsert(): void;
9
+ private appendOutputTable;
10
+ private compileUpsert;
11
+ protected compileSelect(): void;
12
+ protected addLockClause(): void;
13
+ protected compileTruncate(): void;
14
+ /** MSSQL has no RECURSIVE keyword — CTEs are implicitly recursive. */
15
+ protected getCteKeyword(_hasRecursive: boolean): string;
16
16
  }
@@ -3,202 +3,200 @@ import { NativeQueryBuilder } from '../../query/NativeQueryBuilder.js';
3
3
  import { QueryType } from '../../query/enums.js';
4
4
  /** @internal */
5
5
  export class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
6
- compile() {
7
- if (!this.type) {
8
- throw new Error('No query type provided');
9
- }
10
- this.parts.length = 0;
11
- this.params.length = 0;
12
- if (this.options.flags?.has(QueryFlag.IDENTITY_INSERT)) {
13
- this.parts.push(`set identity_insert ${this.getTableName()} on;`);
14
- }
15
- const { prefix, suffix } = this.appendOutputTable();
16
- if (prefix) {
17
- this.parts.push(prefix);
18
- }
19
- if (this.options.comment) {
20
- this.parts.push(...this.options.comment.map(comment => `/* ${comment} */`));
21
- }
22
- this.compileCtes();
23
- if (this.options.onConflict && !Utils.isEmpty(Utils.asArray(this.options.data)[0])) {
24
- this.compileUpsert();
25
- }
26
- else {
27
- switch (this.type) {
28
- case QueryType.SELECT:
29
- case QueryType.COUNT:
30
- this.compileSelect();
31
- break;
32
- case QueryType.INSERT:
33
- this.compileInsert();
34
- break;
35
- case QueryType.UPDATE:
36
- this.compileUpdate();
37
- break;
38
- case QueryType.DELETE:
39
- this.compileDelete();
40
- break;
41
- case QueryType.TRUNCATE:
42
- this.compileTruncate();
43
- break;
44
- }
45
- if (suffix) {
46
- this.parts[this.parts.length - 1] += ';';
47
- this.parts.push(suffix);
48
- }
49
- else if ([QueryType.INSERT, QueryType.UPDATE, QueryType.DELETE].includes(this.type)) {
50
- this.parts[this.parts.length - 1] += '; select @@rowcount;';
51
- }
52
- }
53
- if (this.options.flags?.has(QueryFlag.IDENTITY_INSERT)) {
54
- this.parts.push(`set identity_insert ${this.getTableName()} off;`);
55
- }
56
- return this.combineParts();
57
- }
58
- compileInsert() {
59
- if (!this.options.data) {
60
- throw new Error('No data provided');
61
- }
62
- this.parts.push('insert');
63
- this.addHintComment();
64
- this.parts.push(`into ${this.getTableName()}`);
65
- if (Object.keys(this.options.data).length === 0) {
66
- this.addOutputClause('inserted');
67
- this.parts.push('default values');
68
- return;
69
- }
70
- const parts = this.processInsertData();
71
- if (this.options.flags?.has(QueryFlag.OUTPUT_TABLE)) {
72
- this.parts[this.parts.length - 2] += ' into #out ';
73
- }
74
- this.parts.push(parts.join(', '));
6
+ compile() {
7
+ if (!this.type) {
8
+ throw new Error('No query type provided');
9
+ }
10
+ this.parts.length = 0;
11
+ this.params.length = 0;
12
+ if (this.options.flags?.has(QueryFlag.IDENTITY_INSERT)) {
13
+ this.parts.push(`set identity_insert ${this.getTableName()} on;`);
14
+ }
15
+ const { prefix, suffix } = this.appendOutputTable();
16
+ if (prefix) {
17
+ this.parts.push(prefix);
75
18
  }
76
- appendOutputTable() {
77
- if (!this.options.flags?.has(QueryFlag.OUTPUT_TABLE)) {
78
- return { prefix: '', suffix: '' };
79
- }
80
- const returningFields = this.options.returning;
81
- const selections = returningFields.map(field => `[t].${this.platform.quoteIdentifier(field)}`).join(',');
82
- return {
83
- prefix: `select top(0) ${selections} into #out from ${this.getTableName()} as t left join ${this.getTableName()} on 0 = 1;`,
84
- suffix: `select ${selections} from #out as t; drop table #out`,
85
- };
86
- }
87
- compileUpsert() {
88
- const clause = this.options.onConflict;
89
- const dataAsArray = Utils.asArray(this.options.data);
90
- const keys = Object.keys(dataAsArray[0]);
91
- const values = keys.map(() => '?');
92
- const parts = [];
93
- for (const data of dataAsArray) {
94
- for (const key of keys) {
95
- this.params.push(data[key]);
96
- }
97
- parts.push(`(${values.join(', ')})`);
98
- }
99
- this.parts.push(`merge into ${this.getTableName()}`);
100
- this.parts.push(`using (values ${parts.join(', ')}) as tsource(${keys.map(key => this.quote(key)).join(', ')})`);
101
- if (clause.fields instanceof RawQueryFragment) {
102
- this.parts.push(clause.fields.sql);
103
- this.params.push(...clause.fields.params);
104
- }
105
- else if (clause.fields.length > 0) {
106
- const fields = clause.fields.map(field => {
107
- const col = this.quote(field);
108
- return `${this.getTableName()}.${col} = tsource.${col}`;
109
- });
110
- this.parts.push(`on ${fields.join(' and ')}`);
111
- }
112
- const sourceColumns = keys.map(field => `tsource.${this.quote(field)}`).join(', ');
113
- const destinationColumns = keys.map(field => this.quote(field)).join(', ');
114
- this.parts.push(`when not matched then insert (${destinationColumns}) values (${sourceColumns})`);
115
- if (!clause.ignore) {
116
- this.parts.push('when matched');
117
- if (clause.where) {
118
- this.parts.push(`and ${clause.where.sql}`);
119
- this.params.push(...clause.where.params);
120
- }
121
- this.parts.push('then update set');
122
- if (!clause.merge || Array.isArray(clause.merge)) {
123
- const parts = (clause.merge || keys)
124
- .filter(field => !Array.isArray(clause.fields) || !clause.fields.includes(field))
125
- .map((column) => `${this.quote(column)} = tsource.${this.quote(column)}`);
126
- this.parts.push(parts.join(', '));
127
- }
128
- else if (typeof clause.merge === 'object') {
129
- const parts = Object.entries(clause.merge).map(([key, value]) => {
130
- this.params.push(value);
131
- return `${this.getTableName()}.${this.quote(key)} = ?`;
132
- });
133
- this.parts.push(parts.join(', '));
134
- }
135
- }
136
- this.addOutputClause('inserted');
19
+ if (this.options.comment) {
20
+ this.parts.push(...this.options.comment.map(comment => `/* ${comment} */`));
21
+ }
22
+ this.compileCtes();
23
+ if (this.options.onConflict && !Utils.isEmpty(Utils.asArray(this.options.data)[0])) {
24
+ this.compileUpsert();
25
+ } else {
26
+ switch (this.type) {
27
+ case QueryType.SELECT:
28
+ case QueryType.COUNT:
29
+ this.compileSelect();
30
+ break;
31
+ case QueryType.INSERT:
32
+ this.compileInsert();
33
+ break;
34
+ case QueryType.UPDATE:
35
+ this.compileUpdate();
36
+ break;
37
+ case QueryType.DELETE:
38
+ this.compileDelete();
39
+ break;
40
+ case QueryType.TRUNCATE:
41
+ this.compileTruncate();
42
+ break;
43
+ }
44
+ if (suffix) {
137
45
  this.parts[this.parts.length - 1] += ';';
46
+ this.parts.push(suffix);
47
+ } else if ([QueryType.INSERT, QueryType.UPDATE, QueryType.DELETE].includes(this.type)) {
48
+ this.parts[this.parts.length - 1] += '; select @@rowcount;';
49
+ }
50
+ }
51
+ if (this.options.flags?.has(QueryFlag.IDENTITY_INSERT)) {
52
+ this.parts.push(`set identity_insert ${this.getTableName()} off;`);
53
+ }
54
+ return this.combineParts();
55
+ }
56
+ compileInsert() {
57
+ if (!this.options.data) {
58
+ throw new Error('No data provided');
59
+ }
60
+ this.parts.push('insert');
61
+ this.addHintComment();
62
+ this.parts.push(`into ${this.getTableName()}`);
63
+ if (Object.keys(this.options.data).length === 0) {
64
+ this.addOutputClause('inserted');
65
+ this.parts.push('default values');
66
+ return;
67
+ }
68
+ const parts = this.processInsertData();
69
+ if (this.options.flags?.has(QueryFlag.OUTPUT_TABLE)) {
70
+ this.parts[this.parts.length - 2] += ' into #out ';
71
+ }
72
+ this.parts.push(parts.join(', '));
73
+ }
74
+ appendOutputTable() {
75
+ if (!this.options.flags?.has(QueryFlag.OUTPUT_TABLE)) {
76
+ return { prefix: '', suffix: '' };
77
+ }
78
+ const returningFields = this.options.returning;
79
+ const selections = returningFields.map(field => `[t].${this.platform.quoteIdentifier(field)}`).join(',');
80
+ return {
81
+ prefix: `select top(0) ${selections} into #out from ${this.getTableName()} as t left join ${this.getTableName()} on 0 = 1;`,
82
+ suffix: `select ${selections} from #out as t; drop table #out`,
83
+ };
84
+ }
85
+ compileUpsert() {
86
+ const clause = this.options.onConflict;
87
+ const dataAsArray = Utils.asArray(this.options.data);
88
+ const keys = Object.keys(dataAsArray[0]);
89
+ const values = keys.map(() => '?');
90
+ const parts = [];
91
+ for (const data of dataAsArray) {
92
+ for (const key of keys) {
93
+ this.params.push(data[key]);
94
+ }
95
+ parts.push(`(${values.join(', ')})`);
96
+ }
97
+ this.parts.push(`merge into ${this.getTableName()}`);
98
+ this.parts.push(`using (values ${parts.join(', ')}) as tsource(${keys.map(key => this.quote(key)).join(', ')})`);
99
+ if (clause.fields instanceof RawQueryFragment) {
100
+ this.parts.push(clause.fields.sql);
101
+ this.params.push(...clause.fields.params);
102
+ } else if (clause.fields.length > 0) {
103
+ const fields = clause.fields.map(field => {
104
+ const col = this.quote(field);
105
+ return `${this.getTableName()}.${col} = tsource.${col}`;
106
+ });
107
+ this.parts.push(`on ${fields.join(' and ')}`);
108
+ }
109
+ const sourceColumns = keys.map(field => `tsource.${this.quote(field)}`).join(', ');
110
+ const destinationColumns = keys.map(field => this.quote(field)).join(', ');
111
+ this.parts.push(`when not matched then insert (${destinationColumns}) values (${sourceColumns})`);
112
+ if (!clause.ignore) {
113
+ this.parts.push('when matched');
114
+ if (clause.where) {
115
+ this.parts.push(`and ${clause.where.sql}`);
116
+ this.params.push(...clause.where.params);
117
+ }
118
+ this.parts.push('then update set');
119
+ if (!clause.merge || Array.isArray(clause.merge)) {
120
+ const parts = (clause.merge || keys)
121
+ .filter(field => !Array.isArray(clause.fields) || !clause.fields.includes(field))
122
+ .map(column => `${this.quote(column)} = tsource.${this.quote(column)}`);
123
+ this.parts.push(parts.join(', '));
124
+ } else if (typeof clause.merge === 'object') {
125
+ const parts = Object.entries(clause.merge).map(([key, value]) => {
126
+ this.params.push(value);
127
+ return `${this.getTableName()}.${this.quote(key)} = ?`;
128
+ });
129
+ this.parts.push(parts.join(', '));
130
+ }
131
+ }
132
+ this.addOutputClause('inserted');
133
+ this.parts[this.parts.length - 1] += ';';
134
+ }
135
+ compileSelect() {
136
+ this.parts.push('select');
137
+ if (this.options.limit != null && this.options.offset == null) {
138
+ this.parts.push(`top (?)`);
139
+ this.params.push(this.options.limit);
140
+ }
141
+ this.addHintComment();
142
+ this.parts.push(`${this.getFields()} from ${this.getTableName()}`);
143
+ this.addLockClause();
144
+ if (this.options.joins) {
145
+ for (const join of this.options.joins) {
146
+ this.parts.push(join.sql);
147
+ this.params.push(...join.params);
148
+ }
149
+ }
150
+ if (this.options.where?.sql.trim()) {
151
+ this.parts.push(`where ${this.options.where.sql}`);
152
+ this.params.push(...this.options.where.params);
153
+ }
154
+ if (this.options.groupBy) {
155
+ const fields = this.options.groupBy.map(field => this.quote(field));
156
+ this.parts.push(`group by ${fields.join(', ')}`);
157
+ }
158
+ if (this.options.having) {
159
+ this.parts.push(`having ${this.options.having.sql}`);
160
+ this.params.push(...this.options.having.params);
161
+ }
162
+ if (this.options.orderBy) {
163
+ this.parts.push(`order by ${this.options.orderBy}`);
164
+ }
165
+ if (this.options.offset != null) {
166
+ /* v8 ignore next */
167
+ if (!this.options.orderBy) {
168
+ throw new Error('Order by clause is required for pagination');
169
+ }
170
+ this.parts.push(`offset ? rows`);
171
+ this.params.push(this.options.offset);
172
+ if (this.options.limit != null) {
173
+ this.parts.push(`fetch next ? rows only`);
174
+ this.params.push(this.options.limit);
175
+ }
176
+ }
177
+ }
178
+ addLockClause() {
179
+ if (
180
+ !this.options.lockMode ||
181
+ ![LockMode.PESSIMISTIC_READ, LockMode.PESSIMISTIC_WRITE].includes(this.options.lockMode)
182
+ ) {
183
+ return;
138
184
  }
139
- compileSelect() {
140
- this.parts.push('select');
141
- if (this.options.limit != null && this.options.offset == null) {
142
- this.parts.push(`top (?)`);
143
- this.params.push(this.options.limit);
144
- }
145
- this.addHintComment();
146
- this.parts.push(`${this.getFields()} from ${this.getTableName()}`);
147
- this.addLockClause();
148
- if (this.options.joins) {
149
- for (const join of this.options.joins) {
150
- this.parts.push(join.sql);
151
- this.params.push(...join.params);
152
- }
153
- }
154
- if (this.options.where?.sql.trim()) {
155
- this.parts.push(`where ${this.options.where.sql}`);
156
- this.params.push(...this.options.where.params);
157
- }
158
- if (this.options.groupBy) {
159
- const fields = this.options.groupBy.map(field => this.quote(field));
160
- this.parts.push(`group by ${fields.join(', ')}`);
161
- }
162
- if (this.options.having) {
163
- this.parts.push(`having ${this.options.having.sql}`);
164
- this.params.push(...this.options.having.params);
165
- }
166
- if (this.options.orderBy) {
167
- this.parts.push(`order by ${this.options.orderBy}`);
168
- }
169
- if (this.options.offset != null) {
170
- /* v8 ignore next */
171
- if (!this.options.orderBy) {
172
- throw new Error('Order by clause is required for pagination');
173
- }
174
- this.parts.push(`offset ? rows`);
175
- this.params.push(this.options.offset);
176
- if (this.options.limit != null) {
177
- this.parts.push(`fetch next ? rows only`);
178
- this.params.push(this.options.limit);
179
- }
180
- }
181
- }
182
- addLockClause() {
183
- if (!this.options.lockMode ||
184
- ![LockMode.PESSIMISTIC_READ, LockMode.PESSIMISTIC_WRITE].includes(this.options.lockMode)) {
185
- return;
186
- }
187
- const map = {
188
- [LockMode.PESSIMISTIC_READ]: 'with (holdlock)',
189
- [LockMode.PESSIMISTIC_WRITE]: 'with (updlock)',
190
- };
191
- if (this.options.lockMode !== LockMode.OPTIMISTIC) {
192
- this.parts.push(map[this.options.lockMode]);
193
- }
194
- }
195
- compileTruncate() {
196
- const tableName = this.getTableName();
197
- const sql = `delete from ${tableName}; declare @count int = case @@rowcount when 0 then 1 else 0 end; dbcc checkident ('${tableName.replace(/[[\]]/g, '')}', reseed, @count)`;
198
- this.parts.push(sql);
199
- }
200
- /** MSSQL has no RECURSIVE keyword — CTEs are implicitly recursive. */
201
- getCteKeyword(_hasRecursive) {
202
- return 'with';
185
+ const map = {
186
+ [LockMode.PESSIMISTIC_READ]: 'with (holdlock)',
187
+ [LockMode.PESSIMISTIC_WRITE]: 'with (updlock)',
188
+ };
189
+ if (this.options.lockMode !== LockMode.OPTIMISTIC) {
190
+ this.parts.push(map[this.options.lockMode]);
203
191
  }
192
+ }
193
+ compileTruncate() {
194
+ const tableName = this.getTableName();
195
+ const sql = `delete from ${tableName}; declare @count int = case @@rowcount when 0 then 1 else 0 end; dbcc checkident ('${tableName.replace(/[[\]]/g, '')}', reseed, @count)`;
196
+ this.parts.push(sql);
197
+ }
198
+ /** MSSQL has no RECURSIVE keyword — CTEs are implicitly recursive. */
199
+ getCteKeyword(_hasRecursive) {
200
+ return 'with';
201
+ }
204
202
  }