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