@mikro-orm/sql 7.0.15 → 7.0.16-dev.1

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 (89) hide show
  1. package/AbstractSqlConnection.d.ts +58 -94
  2. package/AbstractSqlConnection.js +238 -235
  3. package/AbstractSqlDriver.d.ts +155 -410
  4. package/AbstractSqlDriver.js +1972 -2100
  5. package/AbstractSqlPlatform.d.ts +76 -86
  6. package/AbstractSqlPlatform.js +167 -169
  7. package/PivotCollectionPersister.d.ts +15 -33
  8. package/PivotCollectionPersister.js +160 -158
  9. package/README.md +1 -1
  10. package/SqlEntityManager.d.ts +22 -67
  11. package/SqlEntityManager.js +38 -54
  12. package/SqlEntityRepository.d.ts +14 -14
  13. package/SqlEntityRepository.js +23 -23
  14. package/SqlMikroORM.d.ts +8 -49
  15. package/SqlMikroORM.js +8 -8
  16. package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +12 -12
  17. package/dialects/mssql/MsSqlNativeQueryBuilder.js +201 -199
  18. package/dialects/mysql/BaseMySqlPlatform.d.ts +46 -65
  19. package/dialects/mysql/BaseMySqlPlatform.js +134 -137
  20. package/dialects/mysql/MySqlExceptionConverter.d.ts +6 -6
  21. package/dialects/mysql/MySqlExceptionConverter.js +77 -91
  22. package/dialects/mysql/MySqlNativeQueryBuilder.d.ts +3 -3
  23. package/dialects/mysql/MySqlNativeQueryBuilder.js +69 -66
  24. package/dialects/mysql/MySqlSchemaHelper.d.ts +39 -58
  25. package/dialects/mysql/MySqlSchemaHelper.js +319 -327
  26. package/dialects/oracledb/OracleDialect.d.ts +52 -81
  27. package/dialects/oracledb/OracleDialect.js +149 -155
  28. package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +12 -12
  29. package/dialects/oracledb/OracleNativeQueryBuilder.js +243 -239
  30. package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +107 -110
  31. package/dialects/postgresql/BasePostgreSqlPlatform.js +369 -370
  32. package/dialects/postgresql/FullTextType.d.ts +6 -10
  33. package/dialects/postgresql/FullTextType.js +51 -51
  34. package/dialects/postgresql/PostgreSqlExceptionConverter.d.ts +5 -5
  35. package/dialects/postgresql/PostgreSqlExceptionConverter.js +43 -55
  36. package/dialects/postgresql/PostgreSqlNativeQueryBuilder.d.ts +1 -1
  37. package/dialects/postgresql/PostgreSqlNativeQueryBuilder.js +4 -4
  38. package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +82 -117
  39. package/dialects/postgresql/PostgreSqlSchemaHelper.js +712 -748
  40. package/dialects/sqlite/BaseSqliteConnection.d.ts +5 -3
  41. package/dialects/sqlite/BaseSqliteConnection.js +19 -21
  42. package/dialects/sqlite/NodeSqliteDialect.d.ts +1 -1
  43. package/dialects/sqlite/NodeSqliteDialect.js +23 -23
  44. package/dialects/sqlite/SqliteDriver.d.ts +1 -1
  45. package/dialects/sqlite/SqliteDriver.js +3 -3
  46. package/dialects/sqlite/SqliteExceptionConverter.d.ts +6 -6
  47. package/dialects/sqlite/SqliteExceptionConverter.js +51 -67
  48. package/dialects/sqlite/SqliteNativeQueryBuilder.d.ts +2 -2
  49. package/dialects/sqlite/SqliteNativeQueryBuilder.js +7 -7
  50. package/dialects/sqlite/SqlitePlatform.d.ts +73 -64
  51. package/dialects/sqlite/SqlitePlatform.js +143 -143
  52. package/dialects/sqlite/SqliteSchemaHelper.d.ts +61 -78
  53. package/dialects/sqlite/SqliteSchemaHelper.js +522 -541
  54. package/package.json +2 -2
  55. package/plugin/index.d.ts +35 -42
  56. package/plugin/index.js +36 -43
  57. package/plugin/transformer.d.ts +102 -136
  58. package/plugin/transformer.js +988 -1010
  59. package/query/ArrayCriteriaNode.d.ts +4 -4
  60. package/query/ArrayCriteriaNode.js +18 -18
  61. package/query/CriteriaNode.d.ts +25 -35
  62. package/query/CriteriaNode.js +132 -142
  63. package/query/CriteriaNodeFactory.d.ts +6 -49
  64. package/query/CriteriaNodeFactory.js +94 -97
  65. package/query/NativeQueryBuilder.d.ts +120 -120
  66. package/query/NativeQueryBuilder.js +501 -507
  67. package/query/ObjectCriteriaNode.d.ts +12 -12
  68. package/query/ObjectCriteriaNode.js +282 -298
  69. package/query/QueryBuilder.d.ts +906 -1558
  70. package/query/QueryBuilder.js +2217 -2346
  71. package/query/QueryBuilderHelper.d.ts +72 -153
  72. package/query/QueryBuilderHelper.js +1032 -1084
  73. package/query/ScalarCriteriaNode.d.ts +3 -3
  74. package/query/ScalarCriteriaNode.js +46 -53
  75. package/query/enums.d.ts +14 -14
  76. package/query/enums.js +14 -14
  77. package/query/raw.d.ts +6 -16
  78. package/query/raw.js +10 -10
  79. package/schema/DatabaseSchema.d.ts +50 -74
  80. package/schema/DatabaseSchema.js +331 -359
  81. package/schema/DatabaseTable.d.ts +73 -96
  82. package/schema/DatabaseTable.js +974 -1046
  83. package/schema/SchemaComparator.d.ts +66 -70
  84. package/schema/SchemaComparator.js +765 -790
  85. package/schema/SchemaHelper.d.ts +97 -128
  86. package/schema/SchemaHelper.js +668 -683
  87. package/schema/SqlSchemaGenerator.d.ts +59 -79
  88. package/schema/SqlSchemaGenerator.js +495 -525
  89. package/typings.d.ts +275 -405
package/SqlMikroORM.d.ts CHANGED
@@ -1,38 +1,13 @@
1
- import {
2
- type AnyEntity,
3
- type EntityClass,
4
- type EntityManager,
5
- type EntityManagerType,
6
- type EntitySchema,
7
- type IDatabaseDriver,
8
- MikroORM,
9
- type Options,
10
- } from '@mikro-orm/core';
1
+ import { type AnyEntity, type EntityClass, type EntityManager, type EntityManagerType, type EntitySchema, type IDatabaseDriver, MikroORM, type Options } from '@mikro-orm/core';
11
2
  import type { AbstractSqlDriver } from './AbstractSqlDriver.js';
12
3
  import type { SqlEntityManager } from './SqlEntityManager.js';
13
4
  /** Configuration options shared by all SQL drivers. */
14
- export type SqlOptions<
15
- D extends AbstractSqlDriver = AbstractSqlDriver,
16
- EM extends SqlEntityManager<D> = SqlEntityManager<D>,
17
- Entities extends (string | EntityClass<AnyEntity> | EntitySchema)[] = (
18
- | string
19
- | EntityClass<AnyEntity>
20
- | EntitySchema
21
- )[],
22
- > = Partial<Options<D, EM, Entities>>;
5
+ export type SqlOptions<D extends AbstractSqlDriver = AbstractSqlDriver, EM extends SqlEntityManager<D> = SqlEntityManager<D>, Entities extends (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]> = Partial<Options<D, EM, Entities>>;
23
6
  /**
24
7
  * Creates a type-safe configuration object for any SQL driver. The driver class
25
8
  * must be passed via `options.driver` (e.g. `SqliteDriver`, `MySqlDriver`, …).
26
9
  */
27
- export declare function defineSqlConfig<
28
- D extends AbstractSqlDriver = AbstractSqlDriver,
29
- EM extends SqlEntityManager<D> = SqlEntityManager<D>,
30
- Entities extends (string | EntityClass<AnyEntity> | EntitySchema)[] = (
31
- | string
32
- | EntityClass<AnyEntity>
33
- | EntitySchema
34
- )[],
35
- >(options: Partial<Options<D, EM, Entities>>): Partial<Options<D, EM, Entities>>;
10
+ export declare function defineSqlConfig<D extends AbstractSqlDriver = AbstractSqlDriver, EM extends SqlEntityManager<D> = SqlEntityManager<D>, Entities extends (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]>(options: Partial<Options<D, EM, Entities>>): Partial<Options<D, EM, Entities>>;
36
11
  /**
37
12
  * Generic entry point for SQL drivers. Use this when consuming `@mikro-orm/sql`
38
13
  * directly with a Kysely dialect; for the bundled driver packages prefer
@@ -40,25 +15,9 @@ export declare function defineSqlConfig<
40
15
  *
41
16
  * @inheritDoc
42
17
  */
43
- export declare class SqlMikroORM<
44
- D extends AbstractSqlDriver = AbstractSqlDriver,
45
- EM extends SqlEntityManager<D> = SqlEntityManager<D>,
46
- Entities extends (string | EntityClass<AnyEntity> | EntitySchema)[] = (
47
- | string
48
- | EntityClass<AnyEntity>
49
- | EntitySchema
50
- )[],
51
- > extends MikroORM<D, EM, Entities> {
52
- /**
53
- * @inheritDoc
54
- */
55
- static init<
56
- D extends IDatabaseDriver = AbstractSqlDriver,
57
- EM extends EntityManager<D> = D[typeof EntityManagerType] & EntityManager<D>,
58
- Entities extends (string | EntityClass<AnyEntity> | EntitySchema)[] = (
59
- | string
60
- | EntityClass<AnyEntity>
61
- | EntitySchema
62
- )[],
63
- >(options: Partial<Options<D, EM, Entities>>): Promise<MikroORM<D, EM, Entities>>;
18
+ export declare class SqlMikroORM<D extends AbstractSqlDriver = AbstractSqlDriver, EM extends SqlEntityManager<D> = SqlEntityManager<D>, Entities extends (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]> extends MikroORM<D, EM, Entities> {
19
+ /**
20
+ * @inheritDoc
21
+ */
22
+ static init<D extends IDatabaseDriver = AbstractSqlDriver, 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>>;
64
23
  }
package/SqlMikroORM.js CHANGED
@@ -1,10 +1,10 @@
1
- import { defineConfig, MikroORM } from '@mikro-orm/core';
1
+ import { defineConfig, MikroORM, } from '@mikro-orm/core';
2
2
  /**
3
3
  * Creates a type-safe configuration object for any SQL driver. The driver class
4
4
  * must be passed via `options.driver` (e.g. `SqliteDriver`, `MySqlDriver`, …).
5
5
  */
6
6
  export function defineSqlConfig(options) {
7
- return defineConfig(options);
7
+ return defineConfig(options);
8
8
  }
9
9
  /**
10
10
  * Generic entry point for SQL drivers. Use this when consuming `@mikro-orm/sql`
@@ -14,10 +14,10 @@ export function defineSqlConfig(options) {
14
14
  * @inheritDoc
15
15
  */
16
16
  export class SqlMikroORM extends MikroORM {
17
- /**
18
- * @inheritDoc
19
- */
20
- static async init(options) {
21
- return super.init(options);
22
- }
17
+ /**
18
+ * @inheritDoc
19
+ */
20
+ static async init(options) {
21
+ return super.init(options);
22
+ }
23
23
  }
@@ -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,211 +3,213 @@ 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
- } 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) {
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 (isRaw(clause.fields)) {
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 ')}`);
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();
108
57
  }
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
- });
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
+ }
129
74
  this.parts.push(parts.join(', '));
130
- }
131
- }
132
- this.addOutputClause('inserted');
133
- this.parts[this.parts.length - 1] += ';';
134
- }
135
- compileSelect() {
136
- const wrapCountSubquery = this.needsCountSubquery();
137
- if (wrapCountSubquery) {
138
- this.parts.push(`select count(*) as ${this.quote('count')} from (`);
139
- }
140
- this.parts.push('select');
141
- // skip top(?) inside the count subquery — it would limit the distinct rows before counting
142
- if (this.options.limit != null && this.options.offset == null && !wrapCountSubquery) {
143
- this.parts.push(`top (?)`);
144
- this.params.push(this.options.limit);
145
- }
146
- this.addHintComment();
147
- this.parts.push(`${this.getFields(wrapCountSubquery)} from ${this.getTableName()}`);
148
- this.addLockClause();
149
- if (this.options.joins) {
150
- for (const join of this.options.joins) {
151
- this.parts.push(join.sql);
152
- this.params.push(...join.params);
153
- }
154
- }
155
- if (this.options.where?.sql.trim()) {
156
- this.parts.push(`where ${this.options.where.sql}`);
157
- this.params.push(...this.options.where.params);
158
- }
159
- if (this.options.groupBy) {
160
- const fields = this.options.groupBy.map(field => this.quote(field));
161
- this.parts.push(`group by ${fields.join(', ')}`);
162
75
  }
163
- if (this.options.having) {
164
- this.parts.push(`having ${this.options.having.sql}`);
165
- this.params.push(...this.options.having.params);
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 (isRaw(clause.fields)) {
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');
137
+ this.parts[this.parts.length - 1] += ';';
166
138
  }
167
- if (!wrapCountSubquery) {
168
- if (this.options.orderBy) {
169
- this.parts.push(`order by ${this.options.orderBy}`);
170
- }
171
- if (this.options.offset != null) {
172
- /* v8 ignore next */
173
- if (!this.options.orderBy) {
174
- throw new Error('Order by clause is required for pagination');
175
- }
176
- this.parts.push(`offset ? rows`);
177
- this.params.push(this.options.offset);
178
- if (this.options.limit != null) {
179
- this.parts.push(`fetch next ? rows only`);
180
- this.params.push(this.options.limit);
181
- }
182
- }
139
+ compileSelect() {
140
+ const wrapCountSubquery = this.needsCountSubquery();
141
+ if (wrapCountSubquery) {
142
+ this.parts.push(`select count(*) as ${this.quote('count')} from (`);
143
+ }
144
+ this.parts.push('select');
145
+ // skip top(?) inside the count subquery — it would limit the distinct rows before counting
146
+ if (this.options.limit != null && this.options.offset == null && !wrapCountSubquery) {
147
+ this.parts.push(`top (?)`);
148
+ this.params.push(this.options.limit);
149
+ }
150
+ this.addHintComment();
151
+ this.parts.push(`${this.getFields(wrapCountSubquery)} from ${this.getTableName()}`);
152
+ this.addLockClause();
153
+ if (this.options.joins) {
154
+ for (const join of this.options.joins) {
155
+ this.parts.push(join.sql);
156
+ this.params.push(...join.params);
157
+ }
158
+ }
159
+ if (this.options.where?.sql.trim()) {
160
+ this.parts.push(`where ${this.options.where.sql}`);
161
+ this.params.push(...this.options.where.params);
162
+ }
163
+ if (this.options.groupBy) {
164
+ const fields = this.options.groupBy.map(field => this.quote(field));
165
+ this.parts.push(`group by ${fields.join(', ')}`);
166
+ }
167
+ if (this.options.having) {
168
+ this.parts.push(`having ${this.options.having.sql}`);
169
+ this.params.push(...this.options.having.params);
170
+ }
171
+ if (!wrapCountSubquery) {
172
+ if (this.options.orderBy) {
173
+ this.parts.push(`order by ${this.options.orderBy}`);
174
+ }
175
+ if (this.options.offset != null) {
176
+ /* v8 ignore next */
177
+ if (!this.options.orderBy) {
178
+ throw new Error('Order by clause is required for pagination');
179
+ }
180
+ this.parts.push(`offset ? rows`);
181
+ this.params.push(this.options.offset);
182
+ if (this.options.limit != null) {
183
+ this.parts.push(`fetch next ? rows only`);
184
+ this.params.push(this.options.limit);
185
+ }
186
+ }
187
+ }
188
+ if (wrapCountSubquery) {
189
+ const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
190
+ this.parts.push(`)${asKeyword}${this.quote('dcnt')}`);
191
+ }
183
192
  }
184
- if (wrapCountSubquery) {
185
- const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
186
- this.parts.push(`)${asKeyword}${this.quote('dcnt')}`);
193
+ addLockClause() {
194
+ if (!this.options.lockMode ||
195
+ ![LockMode.PESSIMISTIC_READ, LockMode.PESSIMISTIC_WRITE].includes(this.options.lockMode)) {
196
+ return;
197
+ }
198
+ const map = {
199
+ [LockMode.PESSIMISTIC_READ]: 'with (holdlock)',
200
+ [LockMode.PESSIMISTIC_WRITE]: 'with (updlock)',
201
+ };
202
+ if (this.options.lockMode !== LockMode.OPTIMISTIC) {
203
+ this.parts.push(map[this.options.lockMode]);
204
+ }
187
205
  }
188
- }
189
- addLockClause() {
190
- if (
191
- !this.options.lockMode ||
192
- ![LockMode.PESSIMISTIC_READ, LockMode.PESSIMISTIC_WRITE].includes(this.options.lockMode)
193
- ) {
194
- return;
206
+ compileTruncate() {
207
+ const tableName = this.getTableName();
208
+ const sql = `delete from ${tableName}; declare @count int = case @@rowcount when 0 then 1 else 0 end; dbcc checkident ('${tableName.replace(/[[\]]/g, '')}', reseed, @count)`;
209
+ this.parts.push(sql);
195
210
  }
196
- const map = {
197
- [LockMode.PESSIMISTIC_READ]: 'with (holdlock)',
198
- [LockMode.PESSIMISTIC_WRITE]: 'with (updlock)',
199
- };
200
- if (this.options.lockMode !== LockMode.OPTIMISTIC) {
201
- this.parts.push(map[this.options.lockMode]);
211
+ /** MSSQL has no RECURSIVE keyword — CTEs are implicitly recursive. */
212
+ getCteKeyword(_hasRecursive) {
213
+ return 'with';
202
214
  }
203
- }
204
- compileTruncate() {
205
- const tableName = this.getTableName();
206
- const sql = `delete from ${tableName}; declare @count int = case @@rowcount when 0 then 1 else 0 end; dbcc checkident ('${tableName.replace(/[[\]]/g, '')}', reseed, @count)`;
207
- this.parts.push(sql);
208
- }
209
- /** MSSQL has no RECURSIVE keyword — CTEs are implicitly recursive. */
210
- getCteKeyword(_hasRecursive) {
211
- return 'with';
212
- }
213
215
  }