knex 2.5.0 → 3.0.0

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 (190) hide show
  1. package/CHANGELOG.md +78 -59
  2. package/CONTRIBUTING.md +194 -194
  3. package/LICENSE +22 -22
  4. package/README.md +149 -147
  5. package/UPGRADING.md +245 -233
  6. package/bin/cli.js +475 -473
  7. package/bin/utils/cli-config-utils.js +212 -210
  8. package/bin/utils/constants.js +7 -7
  9. package/bin/utils/migrationsLister.js +37 -37
  10. package/knex.js +23 -23
  11. package/knex.mjs +11 -11
  12. package/lib/builder-interface-augmenter.js +120 -120
  13. package/lib/client.js +495 -495
  14. package/lib/constants.js +61 -61
  15. package/lib/dialects/better-sqlite3/index.js +77 -77
  16. package/lib/dialects/cockroachdb/crdb-columncompiler.js +14 -14
  17. package/lib/dialects/cockroachdb/crdb-querybuilder.js +11 -11
  18. package/lib/dialects/cockroachdb/crdb-querycompiler.js +122 -122
  19. package/lib/dialects/cockroachdb/crdb-tablecompiler.js +37 -37
  20. package/lib/dialects/cockroachdb/crdb-viewcompiler.js +15 -15
  21. package/lib/dialects/cockroachdb/index.js +86 -86
  22. package/lib/dialects/mssql/index.js +500 -500
  23. package/lib/dialects/mssql/mssql-formatter.js +34 -34
  24. package/lib/dialects/mssql/query/mssql-querycompiler.js +601 -601
  25. package/lib/dialects/mssql/schema/mssql-columncompiler.js +185 -185
  26. package/lib/dialects/mssql/schema/mssql-compiler.js +91 -91
  27. package/lib/dialects/mssql/schema/mssql-tablecompiler.js +378 -378
  28. package/lib/dialects/mssql/schema/mssql-viewcompiler.js +55 -55
  29. package/lib/dialects/mssql/transaction.js +176 -176
  30. package/lib/dialects/mysql/index.js +201 -201
  31. package/lib/dialects/mysql/query/mysql-querycompiler.js +274 -274
  32. package/lib/dialects/mysql/schema/mysql-columncompiler.js +193 -193
  33. package/lib/dialects/mysql/schema/mysql-compiler.js +60 -60
  34. package/lib/dialects/mysql/schema/mysql-tablecompiler.js +381 -381
  35. package/lib/dialects/mysql/schema/mysql-viewbuilder.js +21 -21
  36. package/lib/dialects/mysql/schema/mysql-viewcompiler.js +15 -15
  37. package/lib/dialects/mysql/transaction.js +46 -46
  38. package/lib/dialects/mysql2/index.js +53 -53
  39. package/lib/dialects/mysql2/transaction.js +44 -44
  40. package/lib/dialects/oracle/DEAD_CODE.md +5 -5
  41. package/lib/dialects/oracle/index.js +92 -92
  42. package/lib/dialects/oracle/query/oracle-querycompiler.js +343 -343
  43. package/lib/dialects/oracle/schema/internal/incrementUtils.js +20 -20
  44. package/lib/dialects/oracle/schema/internal/trigger.js +135 -135
  45. package/lib/dialects/oracle/schema/oracle-columnbuilder.js +17 -17
  46. package/lib/dialects/oracle/schema/oracle-columncompiler.js +126 -126
  47. package/lib/dialects/oracle/schema/oracle-compiler.js +122 -122
  48. package/lib/dialects/oracle/schema/oracle-tablecompiler.js +190 -190
  49. package/lib/dialects/oracle/utils.js +87 -87
  50. package/lib/dialects/oracledb/index.js +327 -327
  51. package/lib/dialects/oracledb/query/oracledb-querycompiler.js +481 -481
  52. package/lib/dialects/oracledb/schema/oracledb-columncompiler.js +61 -61
  53. package/lib/dialects/oracledb/schema/oracledb-tablecompiler.js +19 -19
  54. package/lib/dialects/oracledb/schema/oracledb-viewbuilder.js +13 -13
  55. package/lib/dialects/oracledb/schema/oracledb-viewcompiler.js +19 -19
  56. package/lib/dialects/oracledb/transaction.js +98 -98
  57. package/lib/dialects/oracledb/utils.js +208 -208
  58. package/lib/dialects/pgnative/index.js +60 -60
  59. package/lib/dialects/postgres/execution/pg-transaction.js +19 -19
  60. package/lib/dialects/postgres/index.js +358 -358
  61. package/lib/dialects/postgres/query/pg-querybuilder.js +43 -43
  62. package/lib/dialects/postgres/query/pg-querycompiler.js +400 -400
  63. package/lib/dialects/postgres/schema/pg-columncompiler.js +156 -156
  64. package/lib/dialects/postgres/schema/pg-compiler.js +138 -138
  65. package/lib/dialects/postgres/schema/pg-tablecompiler.js +304 -304
  66. package/lib/dialects/postgres/schema/pg-viewbuilder.js +21 -21
  67. package/lib/dialects/postgres/schema/pg-viewcompiler.js +35 -35
  68. package/lib/dialects/redshift/index.js +86 -86
  69. package/lib/dialects/redshift/query/redshift-querycompiler.js +163 -163
  70. package/lib/dialects/redshift/schema/redshift-columnbuilder.js +22 -22
  71. package/lib/dialects/redshift/schema/redshift-columncompiler.js +67 -67
  72. package/lib/dialects/redshift/schema/redshift-compiler.js +14 -14
  73. package/lib/dialects/redshift/schema/redshift-tablecompiler.js +122 -122
  74. package/lib/dialects/redshift/schema/redshift-viewcompiler.js +11 -11
  75. package/lib/dialects/redshift/transaction.js +32 -32
  76. package/lib/dialects/sqlite3/execution/sqlite-transaction.js +25 -25
  77. package/lib/dialects/sqlite3/index.js +250 -250
  78. package/lib/dialects/sqlite3/query/sqlite-querybuilder.js +33 -33
  79. package/lib/dialects/sqlite3/query/sqlite-querycompiler.js +334 -334
  80. package/lib/dialects/sqlite3/schema/ddl.js +400 -400
  81. package/lib/dialects/sqlite3/schema/internal/compiler.js +327 -327
  82. package/lib/dialects/sqlite3/schema/internal/parser-combinator.js +161 -161
  83. package/lib/dialects/sqlite3/schema/internal/parser.js +638 -638
  84. package/lib/dialects/sqlite3/schema/internal/sqlite-ddl-operations.js +41 -41
  85. package/lib/dialects/sqlite3/schema/internal/tokenizer.js +38 -38
  86. package/lib/dialects/sqlite3/schema/internal/utils.js +12 -12
  87. package/lib/dialects/sqlite3/schema/sqlite-columncompiler.js +50 -50
  88. package/lib/dialects/sqlite3/schema/sqlite-compiler.js +80 -80
  89. package/lib/dialects/sqlite3/schema/sqlite-tablecompiler.js +347 -347
  90. package/lib/dialects/sqlite3/schema/sqlite-viewcompiler.js +40 -40
  91. package/lib/execution/batch-insert.js +51 -51
  92. package/lib/execution/internal/delay.js +6 -6
  93. package/lib/execution/internal/ensure-connection-callback.js +41 -41
  94. package/lib/execution/internal/query-executioner.js +62 -62
  95. package/lib/execution/runner.js +325 -325
  96. package/lib/execution/transaction.js +409 -409
  97. package/lib/formatter/formatterUtils.js +42 -42
  98. package/lib/formatter/rawFormatter.js +84 -84
  99. package/lib/formatter/wrappingFormatter.js +250 -250
  100. package/lib/formatter.js +25 -25
  101. package/lib/index.js +3 -3
  102. package/lib/knex-builder/FunctionHelper.js +80 -80
  103. package/lib/knex-builder/Knex.js +59 -59
  104. package/lib/knex-builder/internal/config-resolver.js +57 -57
  105. package/lib/knex-builder/internal/parse-connection.js +87 -87
  106. package/lib/knex-builder/make-knex.js +345 -345
  107. package/lib/logger.js +76 -76
  108. package/lib/migrations/common/MigrationsLoader.js +36 -36
  109. package/lib/migrations/migrate/MigrationGenerator.js +84 -84
  110. package/lib/migrations/migrate/Migrator.js +599 -598
  111. package/lib/migrations/migrate/migrate-stub.js +17 -17
  112. package/lib/migrations/migrate/migration-list-resolver.js +33 -33
  113. package/lib/migrations/migrate/migrator-configuration-merger.js +58 -58
  114. package/lib/migrations/migrate/sources/fs-migrations.js +74 -74
  115. package/lib/migrations/migrate/stub/cjs.stub +15 -15
  116. package/lib/migrations/migrate/stub/coffee.stub +13 -13
  117. package/lib/migrations/migrate/stub/eg.stub +14 -14
  118. package/lib/migrations/migrate/stub/js-schema.stub +22 -22
  119. package/lib/migrations/migrate/stub/js.stub +22 -22
  120. package/lib/migrations/migrate/stub/knexfile-coffee.stub +34 -34
  121. package/lib/migrations/migrate/stub/knexfile-eg.stub +43 -43
  122. package/lib/migrations/migrate/stub/knexfile-js.stub +47 -47
  123. package/lib/migrations/migrate/stub/knexfile-ls.stub +35 -35
  124. package/lib/migrations/migrate/stub/knexfile-ts.stub +47 -47
  125. package/lib/migrations/migrate/stub/ls.stub +14 -14
  126. package/lib/migrations/migrate/stub/mjs.stub +23 -23
  127. package/lib/migrations/migrate/stub/ts-schema.stub +21 -21
  128. package/lib/migrations/migrate/stub/ts.stub +21 -21
  129. package/lib/migrations/migrate/table-creator.js +77 -77
  130. package/lib/migrations/migrate/table-resolver.js +27 -27
  131. package/lib/migrations/seed/Seeder.js +137 -137
  132. package/lib/migrations/seed/seed-stub.js +13 -13
  133. package/lib/migrations/seed/seeder-configuration-merger.js +60 -60
  134. package/lib/migrations/seed/sources/fs-seeds.js +65 -65
  135. package/lib/migrations/seed/stub/coffee.stub +9 -9
  136. package/lib/migrations/seed/stub/eg.stub +11 -11
  137. package/lib/migrations/seed/stub/js.stub +13 -13
  138. package/lib/migrations/seed/stub/ls.stub +11 -11
  139. package/lib/migrations/seed/stub/mjs.stub +12 -12
  140. package/lib/migrations/seed/stub/ts.stub +13 -13
  141. package/lib/migrations/util/fs.js +86 -86
  142. package/lib/migrations/util/import-file.js +12 -12
  143. package/lib/migrations/util/is-module-type.js +9 -9
  144. package/lib/migrations/util/template.js +52 -52
  145. package/lib/migrations/util/timestamp.js +14 -14
  146. package/lib/query/analytic.js +52 -52
  147. package/lib/query/constants.js +15 -15
  148. package/lib/query/joinclause.js +270 -270
  149. package/lib/query/method-constants.js +136 -136
  150. package/lib/query/querybuilder.js +1793 -1793
  151. package/lib/query/querycompiler.js +1591 -1591
  152. package/lib/raw.js +139 -139
  153. package/lib/ref.js +39 -39
  154. package/lib/schema/builder.js +115 -115
  155. package/lib/schema/columnbuilder.js +146 -146
  156. package/lib/schema/columncompiler.js +307 -307
  157. package/lib/schema/compiler.js +187 -187
  158. package/lib/schema/internal/helpers.js +55 -55
  159. package/lib/schema/tablebuilder.js +376 -376
  160. package/lib/schema/tablecompiler.js +433 -433
  161. package/lib/schema/viewbuilder.js +92 -92
  162. package/lib/schema/viewcompiler.js +138 -138
  163. package/lib/util/finally-mixin.js +13 -13
  164. package/lib/util/helpers.js +95 -95
  165. package/lib/util/is.js +32 -32
  166. package/lib/util/nanoid.js +40 -40
  167. package/lib/util/noop.js +1 -1
  168. package/lib/util/save-async-stack.js +14 -14
  169. package/lib/util/security.js +26 -26
  170. package/lib/util/string.js +190 -190
  171. package/lib/util/timeout.js +29 -29
  172. package/package.json +13 -12
  173. package/scripts/build.js +125 -125
  174. package/scripts/clean.js +31 -31
  175. package/scripts/docker-compose.yml +152 -152
  176. package/scripts/next-release-howto.md +24 -24
  177. package/scripts/oracledb-install-driver-libs.sh +82 -82
  178. package/scripts/release.sh +36 -34
  179. package/scripts/runkit-example.js +35 -34
  180. package/scripts/stress-test/README.txt +18 -18
  181. package/scripts/stress-test/docker-compose.yml +57 -57
  182. package/scripts/stress-test/knex-stress-test.js +212 -208
  183. package/scripts/stress-test/mysql2-random-hanging-every-now-and-then.js +149 -145
  184. package/scripts/stress-test/mysql2-sudden-exit-without-error.js +101 -100
  185. package/scripts/stress-test/reconnect-test-mysql-based-drivers.js +188 -184
  186. package/scripts/update_gitignore_for_tsc_output.js +90 -90
  187. package/types/index.d.ts +3272 -3273
  188. package/types/result.d.ts +27 -27
  189. package/types/tables.d.ts +4 -4
  190. package/lib/dialects/index.js +0 -34
@@ -1,601 +1,601 @@
1
- // MSSQL Query Compiler
2
- // ------
3
- const QueryCompiler = require('../../../query/querycompiler');
4
-
5
- const compact = require('lodash/compact');
6
- const identity = require('lodash/identity');
7
- const isEmpty = require('lodash/isEmpty');
8
- const Raw = require('../../../raw.js');
9
- const {
10
- columnize: columnize_,
11
- } = require('../../../formatter/wrappingFormatter');
12
-
13
- const components = [
14
- 'comments',
15
- 'columns',
16
- 'join',
17
- 'lock',
18
- 'where',
19
- 'union',
20
- 'group',
21
- 'having',
22
- 'order',
23
- 'limit',
24
- 'offset',
25
- ];
26
-
27
- class QueryCompiler_MSSQL extends QueryCompiler {
28
- constructor(client, builder, formatter) {
29
- super(client, builder, formatter);
30
-
31
- const { onConflict } = this.single;
32
- if (onConflict) {
33
- throw new Error('.onConflict() is not supported for mssql.');
34
- }
35
-
36
- this._emptyInsertValue = 'default values';
37
- }
38
-
39
- with() {
40
- // WITH RECURSIVE is a syntax error:
41
- // SQL Server does not syntactically distinguish recursive and non-recursive CTEs.
42
- // So mark all statements as non-recursive, generate the SQL, then restore.
43
- // This approach ensures any changes in base class with() get propagated here.
44
- const undoList = [];
45
- if (this.grouped.with) {
46
- for (const stmt of this.grouped.with) {
47
- if (stmt.recursive) {
48
- undoList.push(stmt);
49
- stmt.recursive = false;
50
- }
51
- }
52
- }
53
-
54
- const result = super.with();
55
-
56
- // Restore the recursive markings, in case this same query gets cloned and passed to other drivers.
57
- for (const stmt of undoList) {
58
- stmt.recursive = true;
59
- }
60
- return result;
61
- }
62
-
63
- select() {
64
- const sql = this.with();
65
- const statements = components.map((component) => this[component](this));
66
- return sql + compact(statements).join(' ');
67
- }
68
-
69
- //#region Insert
70
- // Compiles an "insert" query, allowing for multiple
71
- // inserts using a single query statement.
72
- insert() {
73
- if (
74
- this.single.options &&
75
- this.single.options.includeTriggerModifications
76
- ) {
77
- return this.insertWithTriggers();
78
- } else {
79
- return this.standardInsert();
80
- }
81
- }
82
-
83
- insertWithTriggers() {
84
- const insertValues = this.single.insert || [];
85
- const { returning } = this.single;
86
- let sql =
87
- this.with() +
88
- `${this._buildTempTable(returning)}insert into ${this.tableName} `;
89
- const returningSql = returning
90
- ? this._returning('insert', returning, true) + ' '
91
- : '';
92
-
93
- if (Array.isArray(insertValues)) {
94
- if (insertValues.length === 0) {
95
- return '';
96
- }
97
- } else if (typeof insertValues === 'object' && isEmpty(insertValues)) {
98
- return {
99
- sql:
100
- sql +
101
- returningSql +
102
- this._emptyInsertValue +
103
- this._buildReturningSelect(returning),
104
- returning,
105
- };
106
- }
107
- sql += this._buildInsertData(insertValues, returningSql);
108
-
109
- if (returning) {
110
- sql += this._buildReturningSelect(returning);
111
- }
112
-
113
- return {
114
- sql,
115
- returning,
116
- };
117
- }
118
-
119
- _buildInsertData(insertValues, returningSql) {
120
- let sql = '';
121
- const insertData = this._prepInsert(insertValues);
122
- if (typeof insertData === 'string') {
123
- sql += insertData;
124
- } else {
125
- if (insertData.columns.length) {
126
- sql += `(${this.formatter.columnize(insertData.columns)}`;
127
- sql +=
128
- `) ${returningSql}values (` +
129
- this._buildInsertValues(insertData) +
130
- ')';
131
- } else if (insertValues.length === 1 && insertValues[0]) {
132
- sql += returningSql + this._emptyInsertValue;
133
- } else {
134
- return '';
135
- }
136
- }
137
- return sql;
138
- }
139
-
140
- standardInsert() {
141
- const insertValues = this.single.insert || [];
142
- let sql = this.with() + `insert into ${this.tableName} `;
143
- const { returning } = this.single;
144
- const returningSql = returning
145
- ? this._returning('insert', returning) + ' '
146
- : '';
147
-
148
- if (Array.isArray(insertValues)) {
149
- if (insertValues.length === 0) {
150
- return '';
151
- }
152
- } else if (typeof insertValues === 'object' && isEmpty(insertValues)) {
153
- return {
154
- sql: sql + returningSql + this._emptyInsertValue,
155
- returning,
156
- };
157
- }
158
-
159
- sql += this._buildInsertData(insertValues, returningSql);
160
-
161
- return {
162
- sql,
163
- returning,
164
- };
165
- }
166
- //#endregion
167
-
168
- //#region Update
169
- // Compiles an `update` query, allowing for a return value.
170
- update() {
171
- if (
172
- this.single.options &&
173
- this.single.options.includeTriggerModifications
174
- ) {
175
- return this.updateWithTriggers();
176
- } else {
177
- return this.standardUpdate();
178
- }
179
- }
180
-
181
- updateWithTriggers() {
182
- const top = this.top();
183
- const withSQL = this.with();
184
- const updates = this._prepUpdate(this.single.update);
185
- const join = this.join();
186
- const where = this.where();
187
- const order = this.order();
188
- const { returning } = this.single;
189
- const declaredTemp = this._buildTempTable(returning);
190
- return {
191
- sql:
192
- withSQL +
193
- declaredTemp +
194
- `update ${top ? top + ' ' : ''}${this.tableName}` +
195
- ' set ' +
196
- updates.join(', ') +
197
- (returning ? ` ${this._returning('update', returning, true)}` : '') +
198
- (join ? ` from ${this.tableName} ${join}` : '') +
199
- (where ? ` ${where}` : '') +
200
- (order ? ` ${order}` : '') +
201
- (!returning
202
- ? this._returning('rowcount', '@@rowcount')
203
- : this._buildReturningSelect(returning)),
204
- returning: returning || '@@rowcount',
205
- };
206
- }
207
-
208
- _formatGroupsItemValue(value, nulls) {
209
- const column = super._formatGroupsItemValue(value);
210
- // MSSQL dont support 'is null' syntax in order by,
211
- // so we override this function and add MSSQL specific syntax.
212
- if (nulls && !(value instanceof Raw)) {
213
- const collNulls = `IIF(${column} is null,`;
214
- if (nulls === 'first') {
215
- return `${collNulls}0,1)`;
216
- } else if (nulls === 'last') {
217
- return `${collNulls}1,0)`;
218
- }
219
- }
220
- return column;
221
- }
222
-
223
- standardUpdate() {
224
- const top = this.top();
225
- const withSQL = this.with();
226
- const updates = this._prepUpdate(this.single.update);
227
- const join = this.join();
228
- const where = this.where();
229
- const order = this.order();
230
- const { returning } = this.single;
231
- return {
232
- sql:
233
- withSQL +
234
- `update ${top ? top + ' ' : ''}${this.tableName}` +
235
- ' set ' +
236
- updates.join(', ') +
237
- (returning ? ` ${this._returning('update', returning)}` : '') +
238
- (join ? ` from ${this.tableName} ${join}` : '') +
239
- (where ? ` ${where}` : '') +
240
- (order ? ` ${order}` : '') +
241
- (!returning ? this._returning('rowcount', '@@rowcount') : ''),
242
- returning: returning || '@@rowcount',
243
- };
244
- }
245
- //#endregion
246
-
247
- //#region Delete
248
- // Compiles a `delete` query.
249
- del() {
250
- if (
251
- this.single.options &&
252
- this.single.options.includeTriggerModifications
253
- ) {
254
- return this.deleteWithTriggers();
255
- } else {
256
- return this.standardDelete();
257
- }
258
- }
259
-
260
- deleteWithTriggers() {
261
- // Make sure tableName is processed by the formatter first.
262
- const withSQL = this.with();
263
- const { tableName } = this;
264
- const wheres = this.where();
265
- const joins = this.join();
266
- const { returning } = this.single;
267
- const returningStr = returning
268
- ? ` ${this._returning('del', returning, true)}`
269
- : '';
270
- const deleteSelector = joins ? `${tableName}${returningStr} ` : '';
271
- return {
272
- sql:
273
- withSQL +
274
- `${this._buildTempTable(
275
- returning
276
- )}delete ${deleteSelector}from ${tableName}` +
277
- (!joins ? returningStr : '') +
278
- (joins ? ` ${joins}` : '') +
279
- (wheres ? ` ${wheres}` : '') +
280
- (!returning
281
- ? this._returning('rowcount', '@@rowcount')
282
- : this._buildReturningSelect(returning)),
283
- returning: returning || '@@rowcount',
284
- };
285
- }
286
-
287
- standardDelete() {
288
- // Make sure tableName is processed by the formatter first.
289
- const withSQL = this.with();
290
- const { tableName } = this;
291
- const wheres = this.where();
292
- const joins = this.join();
293
- const { returning } = this.single;
294
- const returningStr = returning
295
- ? ` ${this._returning('del', returning)}`
296
- : '';
297
- // returning needs to be before "from" when using join
298
- const deleteSelector = joins ? `${tableName}${returningStr} ` : '';
299
- return {
300
- sql:
301
- withSQL +
302
- `delete ${deleteSelector}from ${tableName}` +
303
- (!joins ? returningStr : '') +
304
- (joins ? ` ${joins}` : '') +
305
- (wheres ? ` ${wheres}` : '') +
306
- (!returning ? this._returning('rowcount', '@@rowcount') : ''),
307
- returning: returning || '@@rowcount',
308
- };
309
- }
310
- //#endregion
311
-
312
- // Compiles the columns in the query, specifying if an item was distinct.
313
- columns() {
314
- let distinctClause = '';
315
- if (this.onlyUnions()) return '';
316
- const top = this.top();
317
- const hints = this._hintComments();
318
- const columns = this.grouped.columns || [];
319
- let i = -1,
320
- sql = [];
321
- if (columns) {
322
- while (++i < columns.length) {
323
- const stmt = columns[i];
324
- if (stmt.distinct) distinctClause = 'distinct ';
325
- if (stmt.distinctOn) {
326
- distinctClause = this.distinctOn(stmt.value);
327
- continue;
328
- }
329
- if (stmt.type === 'aggregate') {
330
- sql.push(...this.aggregate(stmt));
331
- } else if (stmt.type === 'aggregateRaw') {
332
- sql.push(this.aggregateRaw(stmt));
333
- } else if (stmt.type === 'analytic') {
334
- sql.push(this.analytic(stmt));
335
- } else if (stmt.type === 'json') {
336
- sql.push(this.json(stmt));
337
- } else if (stmt.value && stmt.value.length > 0) {
338
- sql.push(this.formatter.columnize(stmt.value));
339
- }
340
- }
341
- }
342
- if (sql.length === 0) sql = ['*'];
343
- const select = this.onlyJson() ? '' : 'select ';
344
- return (
345
- `${select}${hints}${distinctClause}` +
346
- (top ? top + ' ' : '') +
347
- sql.join(', ') +
348
- (this.tableName ? ` from ${this.tableName}` : '')
349
- );
350
- }
351
-
352
- _returning(method, value, withTrigger) {
353
- switch (method) {
354
- case 'update':
355
- case 'insert':
356
- return value
357
- ? `output ${this.formatter.columnizeWithPrefix('inserted.', value)}${
358
- withTrigger ? ' into #out' : ''
359
- }`
360
- : '';
361
- case 'del':
362
- return value
363
- ? `output ${this.formatter.columnizeWithPrefix('deleted.', value)}${
364
- withTrigger ? ' into #out' : ''
365
- }`
366
- : '';
367
- case 'rowcount':
368
- return value ? ';select @@rowcount' : '';
369
- }
370
- }
371
-
372
- _buildTempTable(values) {
373
- // If value is nothing then return an empty string
374
- if (values && values.length > 0) {
375
- let selections = '';
376
-
377
- // Build values that will be returned from this procedure
378
- if (Array.isArray(values)) {
379
- selections = values
380
- .map((value) => `[t].${this.formatter.columnize(value)}`)
381
- .join(',');
382
- } else {
383
- selections = `[t].${this.formatter.columnize(values)}`;
384
- }
385
-
386
- // Force #out to be correctly populated with the correct column structure.
387
- let sql = `select top(0) ${selections} into #out `;
388
- sql += `from ${this.tableName} as t `;
389
- sql += `left join ${this.tableName} on 0=1;`;
390
-
391
- return sql;
392
- }
393
-
394
- return '';
395
- }
396
-
397
- _buildReturningSelect(values) {
398
- // If value is nothing then return an empty string
399
- if (values && values.length > 0) {
400
- let selections = '';
401
-
402
- // Build columns to return
403
- if (Array.isArray(values)) {
404
- selections = values
405
- .map((value) => `${this.formatter.columnize(value)}`)
406
- .join(',');
407
- } else {
408
- selections = this.formatter.columnize(values);
409
- }
410
-
411
- // Get the returned values
412
- let sql = `; select ${selections} from #out; `;
413
- // Drop the temp table to prevent memory leaks
414
- sql += `drop table #out;`;
415
-
416
- return sql;
417
- }
418
-
419
- return '';
420
- }
421
-
422
- // Compiles a `truncate` query.
423
- truncate() {
424
- return `truncate table ${this.tableName}`;
425
- }
426
-
427
- forUpdate() {
428
- // this doesn't work exacltly as it should, one should also mention index while locking
429
- // https://stackoverflow.com/a/9818448/360060
430
- return 'with (UPDLOCK)';
431
- }
432
-
433
- forShare() {
434
- // http://www.sqlteam.com/article/introduction-to-locking-in-sql-server
435
- return 'with (HOLDLOCK)';
436
- }
437
-
438
- // Compiles a `columnInfo` query.
439
- columnInfo() {
440
- const column = this.single.columnInfo;
441
- let schema = this.single.schema;
442
-
443
- // The user may have specified a custom wrapIdentifier function in the config. We
444
- // need to run the identifiers through that function, but not format them as
445
- // identifiers otherwise.
446
- const table = this.client.customWrapIdentifier(this.single.table, identity);
447
-
448
- if (schema) {
449
- schema = this.client.customWrapIdentifier(schema, identity);
450
- }
451
-
452
- // GOTCHA: INFORMATION_SCHEMA.COLUMNS must be capitalized to work when the database has a case-sensitive collation. [#4573]
453
- let sql = `select [COLUMN_NAME], [COLUMN_DEFAULT], [DATA_TYPE], [CHARACTER_MAXIMUM_LENGTH], [IS_NULLABLE] from INFORMATION_SCHEMA.COLUMNS where table_name = ? and table_catalog = ?`;
454
- const bindings = [table, this.client.database()];
455
-
456
- if (schema) {
457
- sql += ' and table_schema = ?';
458
- bindings.push(schema);
459
- } else {
460
- sql += ` and table_schema = 'dbo'`;
461
- }
462
-
463
- return {
464
- sql,
465
- bindings: bindings,
466
- output(resp) {
467
- const out = resp.reduce((columns, val) => {
468
- columns[val[0].value] = {
469
- defaultValue: val[1].value,
470
- type: val[2].value,
471
- maxLength: val[3].value,
472
- nullable: val[4].value === 'YES',
473
- };
474
- return columns;
475
- }, {});
476
- return (column && out[column]) || out;
477
- },
478
- };
479
- }
480
-
481
- top() {
482
- const noLimit = !this.single.limit && this.single.limit !== 0;
483
- const noOffset = !this.single.offset;
484
- if (noLimit || !noOffset) return '';
485
- return `top (${this._getValueOrParameterFromAttribute('limit')})`;
486
- }
487
-
488
- limit() {
489
- return '';
490
- }
491
-
492
- offset() {
493
- const noLimit = !this.single.limit && this.single.limit !== 0;
494
- const noOffset = !this.single.offset;
495
- if (noOffset) return '';
496
- let offset = `offset ${
497
- noOffset ? '0' : this._getValueOrParameterFromAttribute('offset')
498
- } rows`;
499
- if (!noLimit) {
500
- offset += ` fetch next ${this._getValueOrParameterFromAttribute(
501
- 'limit'
502
- )} rows only`;
503
- }
504
- return offset;
505
- }
506
-
507
- whereLike(statement) {
508
- return `${this._columnClause(
509
- statement
510
- )} collate SQL_Latin1_General_CP1_CS_AS ${this._not(
511
- statement,
512
- 'like '
513
- )}${this._valueClause(statement)}`;
514
- }
515
-
516
- whereILike(statement) {
517
- return `${this._columnClause(
518
- statement
519
- )} collate SQL_Latin1_General_CP1_CI_AS ${this._not(
520
- statement,
521
- 'like '
522
- )}${this._valueClause(statement)}`;
523
- }
524
-
525
- jsonExtract(params) {
526
- // JSON_VALUE return NULL if we query object or array
527
- // JSON_QUERY return NULL if we query literal/single value
528
- return this._jsonExtract(
529
- params.singleValue ? 'JSON_VALUE' : 'JSON_QUERY',
530
- params
531
- );
532
- }
533
-
534
- jsonSet(params) {
535
- return this._jsonSet('JSON_MODIFY', params);
536
- }
537
-
538
- jsonInsert(params) {
539
- return this._jsonSet('JSON_MODIFY', params);
540
- }
541
-
542
- jsonRemove(params) {
543
- const jsonCol = `JSON_MODIFY(${columnize_(
544
- params.column,
545
- this.builder,
546
- this.client,
547
- this.bindingsHolder
548
- )},${this.client.parameter(
549
- params.path,
550
- this.builder,
551
- this.bindingsHolder
552
- )}, NULL)`;
553
- return params.alias
554
- ? this.client.alias(jsonCol, this.formatter.wrap(params.alias))
555
- : jsonCol;
556
- }
557
-
558
- whereJsonPath(statement) {
559
- return this._whereJsonPath('JSON_VALUE', statement);
560
- }
561
-
562
- whereJsonSupersetOf(statement) {
563
- throw new Error(
564
- 'Json superset where clause not actually supported by MSSQL'
565
- );
566
- }
567
-
568
- whereJsonSubsetOf(statement) {
569
- throw new Error('Json subset where clause not actually supported by MSSQL');
570
- }
571
-
572
- _getExtracts(statement, operator) {
573
- const column = columnize_(
574
- statement.column,
575
- this.builder,
576
- this.client,
577
- this.bindingsHolder
578
- );
579
- return (
580
- Array.isArray(statement.values) ? statement.values : [statement.values]
581
- )
582
- .map(function (value) {
583
- return (
584
- 'JSON_VALUE(' +
585
- column +
586
- ',' +
587
- this.client.parameter(value, this.builder, this.bindingsHolder) +
588
- ')'
589
- );
590
- }, this)
591
- .join(operator);
592
- }
593
-
594
- onJsonPathEquals(clause) {
595
- return this._onJsonPathEquals('JSON_VALUE', clause);
596
- }
597
- }
598
-
599
- // Set the QueryBuilder & QueryCompiler on the client object,
600
- // in case anyone wants to modify things to suit their own purposes.
601
- module.exports = QueryCompiler_MSSQL;
1
+ // MSSQL Query Compiler
2
+ // ------
3
+ const QueryCompiler = require('../../../query/querycompiler');
4
+
5
+ const compact = require('lodash/compact');
6
+ const identity = require('lodash/identity');
7
+ const isEmpty = require('lodash/isEmpty');
8
+ const Raw = require('../../../raw.js');
9
+ const {
10
+ columnize: columnize_,
11
+ } = require('../../../formatter/wrappingFormatter');
12
+
13
+ const components = [
14
+ 'comments',
15
+ 'columns',
16
+ 'join',
17
+ 'lock',
18
+ 'where',
19
+ 'union',
20
+ 'group',
21
+ 'having',
22
+ 'order',
23
+ 'limit',
24
+ 'offset',
25
+ ];
26
+
27
+ class QueryCompiler_MSSQL extends QueryCompiler {
28
+ constructor(client, builder, formatter) {
29
+ super(client, builder, formatter);
30
+
31
+ const { onConflict } = this.single;
32
+ if (onConflict) {
33
+ throw new Error('.onConflict() is not supported for mssql.');
34
+ }
35
+
36
+ this._emptyInsertValue = 'default values';
37
+ }
38
+
39
+ with() {
40
+ // WITH RECURSIVE is a syntax error:
41
+ // SQL Server does not syntactically distinguish recursive and non-recursive CTEs.
42
+ // So mark all statements as non-recursive, generate the SQL, then restore.
43
+ // This approach ensures any changes in base class with() get propagated here.
44
+ const undoList = [];
45
+ if (this.grouped.with) {
46
+ for (const stmt of this.grouped.with) {
47
+ if (stmt.recursive) {
48
+ undoList.push(stmt);
49
+ stmt.recursive = false;
50
+ }
51
+ }
52
+ }
53
+
54
+ const result = super.with();
55
+
56
+ // Restore the recursive markings, in case this same query gets cloned and passed to other drivers.
57
+ for (const stmt of undoList) {
58
+ stmt.recursive = true;
59
+ }
60
+ return result;
61
+ }
62
+
63
+ select() {
64
+ const sql = this.with();
65
+ const statements = components.map((component) => this[component](this));
66
+ return sql + compact(statements).join(' ');
67
+ }
68
+
69
+ //#region Insert
70
+ // Compiles an "insert" query, allowing for multiple
71
+ // inserts using a single query statement.
72
+ insert() {
73
+ if (
74
+ this.single.options &&
75
+ this.single.options.includeTriggerModifications
76
+ ) {
77
+ return this.insertWithTriggers();
78
+ } else {
79
+ return this.standardInsert();
80
+ }
81
+ }
82
+
83
+ insertWithTriggers() {
84
+ const insertValues = this.single.insert || [];
85
+ const { returning } = this.single;
86
+ let sql =
87
+ this.with() +
88
+ `${this._buildTempTable(returning)}insert into ${this.tableName} `;
89
+ const returningSql = returning
90
+ ? this._returning('insert', returning, true) + ' '
91
+ : '';
92
+
93
+ if (Array.isArray(insertValues)) {
94
+ if (insertValues.length === 0) {
95
+ return '';
96
+ }
97
+ } else if (typeof insertValues === 'object' && isEmpty(insertValues)) {
98
+ return {
99
+ sql:
100
+ sql +
101
+ returningSql +
102
+ this._emptyInsertValue +
103
+ this._buildReturningSelect(returning),
104
+ returning,
105
+ };
106
+ }
107
+ sql += this._buildInsertData(insertValues, returningSql);
108
+
109
+ if (returning) {
110
+ sql += this._buildReturningSelect(returning);
111
+ }
112
+
113
+ return {
114
+ sql,
115
+ returning,
116
+ };
117
+ }
118
+
119
+ _buildInsertData(insertValues, returningSql) {
120
+ let sql = '';
121
+ const insertData = this._prepInsert(insertValues);
122
+ if (typeof insertData === 'string') {
123
+ sql += insertData;
124
+ } else {
125
+ if (insertData.columns.length) {
126
+ sql += `(${this.formatter.columnize(insertData.columns)}`;
127
+ sql +=
128
+ `) ${returningSql}values (` +
129
+ this._buildInsertValues(insertData) +
130
+ ')';
131
+ } else if (insertValues.length === 1 && insertValues[0]) {
132
+ sql += returningSql + this._emptyInsertValue;
133
+ } else {
134
+ return '';
135
+ }
136
+ }
137
+ return sql;
138
+ }
139
+
140
+ standardInsert() {
141
+ const insertValues = this.single.insert || [];
142
+ let sql = this.with() + `insert into ${this.tableName} `;
143
+ const { returning } = this.single;
144
+ const returningSql = returning
145
+ ? this._returning('insert', returning) + ' '
146
+ : '';
147
+
148
+ if (Array.isArray(insertValues)) {
149
+ if (insertValues.length === 0) {
150
+ return '';
151
+ }
152
+ } else if (typeof insertValues === 'object' && isEmpty(insertValues)) {
153
+ return {
154
+ sql: sql + returningSql + this._emptyInsertValue,
155
+ returning,
156
+ };
157
+ }
158
+
159
+ sql += this._buildInsertData(insertValues, returningSql);
160
+
161
+ return {
162
+ sql,
163
+ returning,
164
+ };
165
+ }
166
+ //#endregion
167
+
168
+ //#region Update
169
+ // Compiles an `update` query, allowing for a return value.
170
+ update() {
171
+ if (
172
+ this.single.options &&
173
+ this.single.options.includeTriggerModifications
174
+ ) {
175
+ return this.updateWithTriggers();
176
+ } else {
177
+ return this.standardUpdate();
178
+ }
179
+ }
180
+
181
+ updateWithTriggers() {
182
+ const top = this.top();
183
+ const withSQL = this.with();
184
+ const updates = this._prepUpdate(this.single.update);
185
+ const join = this.join();
186
+ const where = this.where();
187
+ const order = this.order();
188
+ const { returning } = this.single;
189
+ const declaredTemp = this._buildTempTable(returning);
190
+ return {
191
+ sql:
192
+ withSQL +
193
+ declaredTemp +
194
+ `update ${top ? top + ' ' : ''}${this.tableName}` +
195
+ ' set ' +
196
+ updates.join(', ') +
197
+ (returning ? ` ${this._returning('update', returning, true)}` : '') +
198
+ (join ? ` from ${this.tableName} ${join}` : '') +
199
+ (where ? ` ${where}` : '') +
200
+ (order ? ` ${order}` : '') +
201
+ (!returning
202
+ ? this._returning('rowcount', '@@rowcount')
203
+ : this._buildReturningSelect(returning)),
204
+ returning: returning || '@@rowcount',
205
+ };
206
+ }
207
+
208
+ _formatGroupsItemValue(value, nulls) {
209
+ const column = super._formatGroupsItemValue(value);
210
+ // MSSQL dont support 'is null' syntax in order by,
211
+ // so we override this function and add MSSQL specific syntax.
212
+ if (nulls && !(value instanceof Raw)) {
213
+ const collNulls = `IIF(${column} is null,`;
214
+ if (nulls === 'first') {
215
+ return `${collNulls}0,1)`;
216
+ } else if (nulls === 'last') {
217
+ return `${collNulls}1,0)`;
218
+ }
219
+ }
220
+ return column;
221
+ }
222
+
223
+ standardUpdate() {
224
+ const top = this.top();
225
+ const withSQL = this.with();
226
+ const updates = this._prepUpdate(this.single.update);
227
+ const join = this.join();
228
+ const where = this.where();
229
+ const order = this.order();
230
+ const { returning } = this.single;
231
+ return {
232
+ sql:
233
+ withSQL +
234
+ `update ${top ? top + ' ' : ''}${this.tableName}` +
235
+ ' set ' +
236
+ updates.join(', ') +
237
+ (returning ? ` ${this._returning('update', returning)}` : '') +
238
+ (join ? ` from ${this.tableName} ${join}` : '') +
239
+ (where ? ` ${where}` : '') +
240
+ (order ? ` ${order}` : '') +
241
+ (!returning ? this._returning('rowcount', '@@rowcount') : ''),
242
+ returning: returning || '@@rowcount',
243
+ };
244
+ }
245
+ //#endregion
246
+
247
+ //#region Delete
248
+ // Compiles a `delete` query.
249
+ del() {
250
+ if (
251
+ this.single.options &&
252
+ this.single.options.includeTriggerModifications
253
+ ) {
254
+ return this.deleteWithTriggers();
255
+ } else {
256
+ return this.standardDelete();
257
+ }
258
+ }
259
+
260
+ deleteWithTriggers() {
261
+ // Make sure tableName is processed by the formatter first.
262
+ const withSQL = this.with();
263
+ const { tableName } = this;
264
+ const wheres = this.where();
265
+ const joins = this.join();
266
+ const { returning } = this.single;
267
+ const returningStr = returning
268
+ ? ` ${this._returning('del', returning, true)}`
269
+ : '';
270
+ const deleteSelector = joins ? `${tableName}${returningStr} ` : '';
271
+ return {
272
+ sql:
273
+ withSQL +
274
+ `${this._buildTempTable(
275
+ returning
276
+ )}delete ${deleteSelector}from ${tableName}` +
277
+ (!joins ? returningStr : '') +
278
+ (joins ? ` ${joins}` : '') +
279
+ (wheres ? ` ${wheres}` : '') +
280
+ (!returning
281
+ ? this._returning('rowcount', '@@rowcount')
282
+ : this._buildReturningSelect(returning)),
283
+ returning: returning || '@@rowcount',
284
+ };
285
+ }
286
+
287
+ standardDelete() {
288
+ // Make sure tableName is processed by the formatter first.
289
+ const withSQL = this.with();
290
+ const { tableName } = this;
291
+ const wheres = this.where();
292
+ const joins = this.join();
293
+ const { returning } = this.single;
294
+ const returningStr = returning
295
+ ? ` ${this._returning('del', returning)}`
296
+ : '';
297
+ // returning needs to be before "from" when using join
298
+ const deleteSelector = joins ? `${tableName}${returningStr} ` : '';
299
+ return {
300
+ sql:
301
+ withSQL +
302
+ `delete ${deleteSelector}from ${tableName}` +
303
+ (!joins ? returningStr : '') +
304
+ (joins ? ` ${joins}` : '') +
305
+ (wheres ? ` ${wheres}` : '') +
306
+ (!returning ? this._returning('rowcount', '@@rowcount') : ''),
307
+ returning: returning || '@@rowcount',
308
+ };
309
+ }
310
+ //#endregion
311
+
312
+ // Compiles the columns in the query, specifying if an item was distinct.
313
+ columns() {
314
+ let distinctClause = '';
315
+ if (this.onlyUnions()) return '';
316
+ const top = this.top();
317
+ const hints = this._hintComments();
318
+ const columns = this.grouped.columns || [];
319
+ let i = -1,
320
+ sql = [];
321
+ if (columns) {
322
+ while (++i < columns.length) {
323
+ const stmt = columns[i];
324
+ if (stmt.distinct) distinctClause = 'distinct ';
325
+ if (stmt.distinctOn) {
326
+ distinctClause = this.distinctOn(stmt.value);
327
+ continue;
328
+ }
329
+ if (stmt.type === 'aggregate') {
330
+ sql.push(...this.aggregate(stmt));
331
+ } else if (stmt.type === 'aggregateRaw') {
332
+ sql.push(this.aggregateRaw(stmt));
333
+ } else if (stmt.type === 'analytic') {
334
+ sql.push(this.analytic(stmt));
335
+ } else if (stmt.type === 'json') {
336
+ sql.push(this.json(stmt));
337
+ } else if (stmt.value && stmt.value.length > 0) {
338
+ sql.push(this.formatter.columnize(stmt.value));
339
+ }
340
+ }
341
+ }
342
+ if (sql.length === 0) sql = ['*'];
343
+ const select = this.onlyJson() ? '' : 'select ';
344
+ return (
345
+ `${select}${hints}${distinctClause}` +
346
+ (top ? top + ' ' : '') +
347
+ sql.join(', ') +
348
+ (this.tableName ? ` from ${this.tableName}` : '')
349
+ );
350
+ }
351
+
352
+ _returning(method, value, withTrigger) {
353
+ switch (method) {
354
+ case 'update':
355
+ case 'insert':
356
+ return value
357
+ ? `output ${this.formatter.columnizeWithPrefix('inserted.', value)}${
358
+ withTrigger ? ' into #out' : ''
359
+ }`
360
+ : '';
361
+ case 'del':
362
+ return value
363
+ ? `output ${this.formatter.columnizeWithPrefix('deleted.', value)}${
364
+ withTrigger ? ' into #out' : ''
365
+ }`
366
+ : '';
367
+ case 'rowcount':
368
+ return value ? ';select @@rowcount' : '';
369
+ }
370
+ }
371
+
372
+ _buildTempTable(values) {
373
+ // If value is nothing then return an empty string
374
+ if (values && values.length > 0) {
375
+ let selections = '';
376
+
377
+ // Build values that will be returned from this procedure
378
+ if (Array.isArray(values)) {
379
+ selections = values
380
+ .map((value) => `[t].${this.formatter.columnize(value)}`)
381
+ .join(',');
382
+ } else {
383
+ selections = `[t].${this.formatter.columnize(values)}`;
384
+ }
385
+
386
+ // Force #out to be correctly populated with the correct column structure.
387
+ let sql = `select top(0) ${selections} into #out `;
388
+ sql += `from ${this.tableName} as t `;
389
+ sql += `left join ${this.tableName} on 0=1;`;
390
+
391
+ return sql;
392
+ }
393
+
394
+ return '';
395
+ }
396
+
397
+ _buildReturningSelect(values) {
398
+ // If value is nothing then return an empty string
399
+ if (values && values.length > 0) {
400
+ let selections = '';
401
+
402
+ // Build columns to return
403
+ if (Array.isArray(values)) {
404
+ selections = values
405
+ .map((value) => `${this.formatter.columnize(value)}`)
406
+ .join(',');
407
+ } else {
408
+ selections = this.formatter.columnize(values);
409
+ }
410
+
411
+ // Get the returned values
412
+ let sql = `; select ${selections} from #out; `;
413
+ // Drop the temp table to prevent memory leaks
414
+ sql += `drop table #out;`;
415
+
416
+ return sql;
417
+ }
418
+
419
+ return '';
420
+ }
421
+
422
+ // Compiles a `truncate` query.
423
+ truncate() {
424
+ return `truncate table ${this.tableName}`;
425
+ }
426
+
427
+ forUpdate() {
428
+ // this doesn't work exacltly as it should, one should also mention index while locking
429
+ // https://stackoverflow.com/a/9818448/360060
430
+ return 'with (UPDLOCK)';
431
+ }
432
+
433
+ forShare() {
434
+ // http://www.sqlteam.com/article/introduction-to-locking-in-sql-server
435
+ return 'with (HOLDLOCK)';
436
+ }
437
+
438
+ // Compiles a `columnInfo` query.
439
+ columnInfo() {
440
+ const column = this.single.columnInfo;
441
+ let schema = this.single.schema;
442
+
443
+ // The user may have specified a custom wrapIdentifier function in the config. We
444
+ // need to run the identifiers through that function, but not format them as
445
+ // identifiers otherwise.
446
+ const table = this.client.customWrapIdentifier(this.single.table, identity);
447
+
448
+ if (schema) {
449
+ schema = this.client.customWrapIdentifier(schema, identity);
450
+ }
451
+
452
+ // GOTCHA: INFORMATION_SCHEMA.COLUMNS must be capitalized to work when the database has a case-sensitive collation. [#4573]
453
+ let sql = `select [COLUMN_NAME], [COLUMN_DEFAULT], [DATA_TYPE], [CHARACTER_MAXIMUM_LENGTH], [IS_NULLABLE] from INFORMATION_SCHEMA.COLUMNS where table_name = ? and table_catalog = ?`;
454
+ const bindings = [table, this.client.database()];
455
+
456
+ if (schema) {
457
+ sql += ' and table_schema = ?';
458
+ bindings.push(schema);
459
+ } else {
460
+ sql += ` and table_schema = 'dbo'`;
461
+ }
462
+
463
+ return {
464
+ sql,
465
+ bindings: bindings,
466
+ output(resp) {
467
+ const out = resp.reduce((columns, val) => {
468
+ columns[val[0].value] = {
469
+ defaultValue: val[1].value,
470
+ type: val[2].value,
471
+ maxLength: val[3].value,
472
+ nullable: val[4].value === 'YES',
473
+ };
474
+ return columns;
475
+ }, {});
476
+ return (column && out[column]) || out;
477
+ },
478
+ };
479
+ }
480
+
481
+ top() {
482
+ const noLimit = !this.single.limit && this.single.limit !== 0;
483
+ const noOffset = !this.single.offset;
484
+ if (noLimit || !noOffset) return '';
485
+ return `top (${this._getValueOrParameterFromAttribute('limit')})`;
486
+ }
487
+
488
+ limit() {
489
+ return '';
490
+ }
491
+
492
+ offset() {
493
+ const noLimit = !this.single.limit && this.single.limit !== 0;
494
+ const noOffset = !this.single.offset;
495
+ if (noOffset) return '';
496
+ let offset = `offset ${
497
+ noOffset ? '0' : this._getValueOrParameterFromAttribute('offset')
498
+ } rows`;
499
+ if (!noLimit) {
500
+ offset += ` fetch next ${this._getValueOrParameterFromAttribute(
501
+ 'limit'
502
+ )} rows only`;
503
+ }
504
+ return offset;
505
+ }
506
+
507
+ whereLike(statement) {
508
+ return `${this._columnClause(
509
+ statement
510
+ )} collate SQL_Latin1_General_CP1_CS_AS ${this._not(
511
+ statement,
512
+ 'like '
513
+ )}${this._valueClause(statement)}`;
514
+ }
515
+
516
+ whereILike(statement) {
517
+ return `${this._columnClause(
518
+ statement
519
+ )} collate SQL_Latin1_General_CP1_CI_AS ${this._not(
520
+ statement,
521
+ 'like '
522
+ )}${this._valueClause(statement)}`;
523
+ }
524
+
525
+ jsonExtract(params) {
526
+ // JSON_VALUE return NULL if we query object or array
527
+ // JSON_QUERY return NULL if we query literal/single value
528
+ return this._jsonExtract(
529
+ params.singleValue ? 'JSON_VALUE' : 'JSON_QUERY',
530
+ params
531
+ );
532
+ }
533
+
534
+ jsonSet(params) {
535
+ return this._jsonSet('JSON_MODIFY', params);
536
+ }
537
+
538
+ jsonInsert(params) {
539
+ return this._jsonSet('JSON_MODIFY', params);
540
+ }
541
+
542
+ jsonRemove(params) {
543
+ const jsonCol = `JSON_MODIFY(${columnize_(
544
+ params.column,
545
+ this.builder,
546
+ this.client,
547
+ this.bindingsHolder
548
+ )},${this.client.parameter(
549
+ params.path,
550
+ this.builder,
551
+ this.bindingsHolder
552
+ )}, NULL)`;
553
+ return params.alias
554
+ ? this.client.alias(jsonCol, this.formatter.wrap(params.alias))
555
+ : jsonCol;
556
+ }
557
+
558
+ whereJsonPath(statement) {
559
+ return this._whereJsonPath('JSON_VALUE', statement);
560
+ }
561
+
562
+ whereJsonSupersetOf(statement) {
563
+ throw new Error(
564
+ 'Json superset where clause not actually supported by MSSQL'
565
+ );
566
+ }
567
+
568
+ whereJsonSubsetOf(statement) {
569
+ throw new Error('Json subset where clause not actually supported by MSSQL');
570
+ }
571
+
572
+ _getExtracts(statement, operator) {
573
+ const column = columnize_(
574
+ statement.column,
575
+ this.builder,
576
+ this.client,
577
+ this.bindingsHolder
578
+ );
579
+ return (
580
+ Array.isArray(statement.values) ? statement.values : [statement.values]
581
+ )
582
+ .map(function (value) {
583
+ return (
584
+ 'JSON_VALUE(' +
585
+ column +
586
+ ',' +
587
+ this.client.parameter(value, this.builder, this.bindingsHolder) +
588
+ ')'
589
+ );
590
+ }, this)
591
+ .join(operator);
592
+ }
593
+
594
+ onJsonPathEquals(clause) {
595
+ return this._onJsonPathEquals('JSON_VALUE', clause);
596
+ }
597
+ }
598
+
599
+ // Set the QueryBuilder & QueryCompiler on the client object,
600
+ // in case anyone wants to modify things to suit their own purposes.
601
+ module.exports = QueryCompiler_MSSQL;