knex 2.4.1 → 2.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/CONTRIBUTING.md +194 -194
  3. package/LICENSE +22 -22
  4. package/README.md +148 -148
  5. package/UPGRADING.md +233 -233
  6. package/bin/cli.js +473 -472
  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/lib/builder-interface-augmenter.js +120 -120
  12. package/lib/client.js +475 -475
  13. package/lib/constants.js +61 -61
  14. package/lib/dialects/better-sqlite3/index.js +72 -72
  15. package/lib/dialects/cockroachdb/crdb-columncompiler.js +14 -14
  16. package/lib/dialects/cockroachdb/crdb-querybuilder.js +11 -11
  17. package/lib/dialects/cockroachdb/crdb-querycompiler.js +122 -122
  18. package/lib/dialects/cockroachdb/crdb-tablecompiler.js +37 -37
  19. package/lib/dialects/cockroachdb/crdb-viewcompiler.js +15 -15
  20. package/lib/dialects/cockroachdb/index.js +86 -86
  21. package/lib/dialects/mssql/index.js +495 -495
  22. package/lib/dialects/mssql/mssql-formatter.js +34 -34
  23. package/lib/dialects/mssql/query/mssql-querycompiler.js +600 -600
  24. package/lib/dialects/mssql/schema/mssql-columncompiler.js +185 -185
  25. package/lib/dialects/mssql/schema/mssql-compiler.js +91 -91
  26. package/lib/dialects/mssql/schema/mssql-tablecompiler.js +378 -378
  27. package/lib/dialects/mssql/schema/mssql-viewcompiler.js +55 -55
  28. package/lib/dialects/mssql/transaction.js +176 -176
  29. package/lib/dialects/mysql/index.js +201 -201
  30. package/lib/dialects/mysql/query/mysql-querycompiler.js +274 -274
  31. package/lib/dialects/mysql/schema/mysql-columncompiler.js +193 -193
  32. package/lib/dialects/mysql/schema/mysql-compiler.js +60 -60
  33. package/lib/dialects/mysql/schema/mysql-tablecompiler.js +381 -381
  34. package/lib/dialects/mysql/schema/mysql-viewbuilder.js +21 -21
  35. package/lib/dialects/mysql/schema/mysql-viewcompiler.js +15 -15
  36. package/lib/dialects/mysql/transaction.js +46 -46
  37. package/lib/dialects/mysql2/index.js +33 -33
  38. package/lib/dialects/mysql2/transaction.js +44 -44
  39. package/lib/dialects/oracle/DEAD_CODE.md +5 -5
  40. package/lib/dialects/oracle/index.js +92 -92
  41. package/lib/dialects/oracle/query/oracle-querycompiler.js +342 -342
  42. package/lib/dialects/oracle/schema/internal/incrementUtils.js +20 -20
  43. package/lib/dialects/oracle/schema/internal/trigger.js +135 -135
  44. package/lib/dialects/oracle/schema/oracle-columnbuilder.js +17 -17
  45. package/lib/dialects/oracle/schema/oracle-columncompiler.js +126 -126
  46. package/lib/dialects/oracle/schema/oracle-compiler.js +122 -122
  47. package/lib/dialects/oracle/schema/oracle-tablecompiler.js +190 -190
  48. package/lib/dialects/oracle/utils.js +87 -87
  49. package/lib/dialects/oracledb/index.js +327 -327
  50. package/lib/dialects/oracledb/query/oracledb-querycompiler.js +481 -481
  51. package/lib/dialects/oracledb/schema/oracledb-columncompiler.js +55 -55
  52. package/lib/dialects/oracledb/schema/oracledb-tablecompiler.js +19 -19
  53. package/lib/dialects/oracledb/schema/oracledb-viewbuilder.js +13 -13
  54. package/lib/dialects/oracledb/schema/oracledb-viewcompiler.js +19 -19
  55. package/lib/dialects/oracledb/transaction.js +98 -98
  56. package/lib/dialects/oracledb/utils.js +208 -208
  57. package/lib/dialects/pgnative/index.js +60 -60
  58. package/lib/dialects/postgres/execution/pg-transaction.js +12 -12
  59. package/lib/dialects/postgres/index.js +358 -358
  60. package/lib/dialects/postgres/query/pg-querybuilder.js +38 -38
  61. package/lib/dialects/postgres/query/pg-querycompiler.js +395 -395
  62. package/lib/dialects/postgres/schema/pg-columncompiler.js +156 -156
  63. package/lib/dialects/postgres/schema/pg-compiler.js +138 -136
  64. package/lib/dialects/postgres/schema/pg-tablecompiler.js +299 -299
  65. package/lib/dialects/postgres/schema/pg-viewbuilder.js +21 -21
  66. package/lib/dialects/postgres/schema/pg-viewcompiler.js +35 -35
  67. package/lib/dialects/redshift/index.js +86 -86
  68. package/lib/dialects/redshift/query/redshift-querycompiler.js +163 -163
  69. package/lib/dialects/redshift/schema/redshift-columnbuilder.js +22 -22
  70. package/lib/dialects/redshift/schema/redshift-columncompiler.js +67 -67
  71. package/lib/dialects/redshift/schema/redshift-compiler.js +14 -14
  72. package/lib/dialects/redshift/schema/redshift-tablecompiler.js +122 -122
  73. package/lib/dialects/redshift/schema/redshift-viewcompiler.js +11 -11
  74. package/lib/dialects/redshift/transaction.js +25 -25
  75. package/lib/dialects/sqlite3/execution/sqlite-transaction.js +18 -18
  76. package/lib/dialects/sqlite3/index.js +250 -250
  77. package/lib/dialects/sqlite3/query/sqlite-querybuilder.js +33 -33
  78. package/lib/dialects/sqlite3/query/sqlite-querycompiler.js +334 -334
  79. package/lib/dialects/sqlite3/schema/ddl.js +400 -400
  80. package/lib/dialects/sqlite3/schema/internal/compiler.js +327 -327
  81. package/lib/dialects/sqlite3/schema/internal/parser-combinator.js +161 -161
  82. package/lib/dialects/sqlite3/schema/internal/parser.js +638 -638
  83. package/lib/dialects/sqlite3/schema/internal/sqlite-ddl-operations.js +41 -41
  84. package/lib/dialects/sqlite3/schema/internal/tokenizer.js +38 -38
  85. package/lib/dialects/sqlite3/schema/internal/utils.js +12 -12
  86. package/lib/dialects/sqlite3/schema/sqlite-columncompiler.js +50 -50
  87. package/lib/dialects/sqlite3/schema/sqlite-compiler.js +80 -80
  88. package/lib/dialects/sqlite3/schema/sqlite-tablecompiler.js +347 -347
  89. package/lib/dialects/sqlite3/schema/sqlite-viewcompiler.js +40 -40
  90. package/lib/execution/batch-insert.js +51 -51
  91. package/lib/execution/internal/delay.js +6 -6
  92. package/lib/execution/internal/ensure-connection-callback.js +41 -41
  93. package/lib/execution/internal/query-executioner.js +62 -62
  94. package/lib/execution/runner.js +307 -307
  95. package/lib/execution/transaction.js +401 -401
  96. package/lib/formatter/formatterUtils.js +42 -42
  97. package/lib/formatter/rawFormatter.js +84 -84
  98. package/lib/formatter/wrappingFormatter.js +250 -250
  99. package/lib/formatter.js +25 -25
  100. package/lib/index.js +3 -3
  101. package/lib/knex-builder/FunctionHelper.js +54 -54
  102. package/lib/knex-builder/Knex.js +59 -59
  103. package/lib/knex-builder/internal/config-resolver.js +57 -57
  104. package/lib/knex-builder/internal/parse-connection.js +87 -87
  105. package/lib/knex-builder/make-knex.js +340 -340
  106. package/lib/logger.js +76 -76
  107. package/lib/migrations/common/MigrationsLoader.js +36 -36
  108. package/lib/migrations/migrate/MigrationGenerator.js +84 -82
  109. package/lib/migrations/migrate/Migrator.js +598 -598
  110. package/lib/migrations/migrate/migrate-stub.js +17 -17
  111. package/lib/migrations/migrate/migration-list-resolver.js +33 -33
  112. package/lib/migrations/migrate/migrator-configuration-merger.js +58 -58
  113. package/lib/migrations/migrate/sources/fs-migrations.js +74 -74
  114. package/lib/migrations/migrate/stub/cjs.stub +15 -15
  115. package/lib/migrations/migrate/stub/coffee.stub +13 -13
  116. package/lib/migrations/migrate/stub/eg.stub +14 -14
  117. package/lib/migrations/migrate/stub/js-schema.stub +22 -22
  118. package/lib/migrations/migrate/stub/js.stub +22 -22
  119. package/lib/migrations/migrate/stub/knexfile-coffee.stub +34 -34
  120. package/lib/migrations/migrate/stub/knexfile-eg.stub +43 -43
  121. package/lib/migrations/migrate/stub/knexfile-js.stub +47 -47
  122. package/lib/migrations/migrate/stub/knexfile-ls.stub +35 -35
  123. package/lib/migrations/migrate/stub/knexfile-ts.stub +47 -47
  124. package/lib/migrations/migrate/stub/ls.stub +14 -14
  125. package/lib/migrations/migrate/stub/mjs.stub +23 -23
  126. package/lib/migrations/migrate/stub/ts-schema.stub +21 -21
  127. package/lib/migrations/migrate/stub/ts.stub +21 -21
  128. package/lib/migrations/migrate/table-creator.js +77 -77
  129. package/lib/migrations/migrate/table-resolver.js +27 -27
  130. package/lib/migrations/seed/Seeder.js +137 -137
  131. package/lib/migrations/seed/seed-stub.js +13 -13
  132. package/lib/migrations/seed/seeder-configuration-merger.js +60 -60
  133. package/lib/migrations/seed/sources/fs-seeds.js +65 -65
  134. package/lib/migrations/seed/stub/coffee.stub +9 -9
  135. package/lib/migrations/seed/stub/eg.stub +11 -11
  136. package/lib/migrations/seed/stub/js.stub +13 -13
  137. package/lib/migrations/seed/stub/ls.stub +11 -11
  138. package/lib/migrations/seed/stub/mjs.stub +12 -12
  139. package/lib/migrations/seed/stub/ts.stub +13 -13
  140. package/lib/migrations/util/fs.js +86 -86
  141. package/lib/migrations/util/import-file.js +12 -12
  142. package/lib/migrations/util/is-module-type.js +9 -9
  143. package/lib/migrations/util/template.js +52 -52
  144. package/lib/migrations/util/timestamp.js +14 -14
  145. package/lib/query/analytic.js +52 -52
  146. package/lib/query/constants.js +15 -15
  147. package/lib/query/joinclause.js +270 -270
  148. package/lib/query/method-constants.js +135 -135
  149. package/lib/query/querybuilder.js +1794 -1794
  150. package/lib/query/querycompiler.js +1580 -1580
  151. package/lib/raw.js +139 -139
  152. package/lib/ref.js +39 -39
  153. package/lib/schema/builder.js +115 -114
  154. package/lib/schema/columnbuilder.js +146 -145
  155. package/lib/schema/columncompiler.js +307 -307
  156. package/lib/schema/compiler.js +187 -187
  157. package/lib/schema/internal/helpers.js +55 -55
  158. package/lib/schema/tablebuilder.js +376 -375
  159. package/lib/schema/tablecompiler.js +433 -433
  160. package/lib/schema/viewbuilder.js +92 -93
  161. package/lib/schema/viewcompiler.js +138 -138
  162. package/lib/util/finally-mixin.js +13 -13
  163. package/lib/util/helpers.js +95 -95
  164. package/lib/util/is.js +32 -32
  165. package/lib/util/nanoid.js +40 -40
  166. package/lib/util/noop.js +1 -1
  167. package/lib/util/save-async-stack.js +14 -14
  168. package/lib/util/string.js +190 -190
  169. package/lib/util/timeout.js +29 -29
  170. package/package.json +8 -6
  171. package/scripts/build.js +125 -125
  172. package/scripts/clean.js +31 -29
  173. package/scripts/docker-compose.yml +152 -152
  174. package/scripts/next-release-howto.md +24 -24
  175. package/scripts/oracledb-install-driver-libs.sh +82 -82
  176. package/scripts/release.sh +34 -34
  177. package/scripts/runkit-example.js +34 -34
  178. package/scripts/stress-test/README.txt +18 -18
  179. package/scripts/stress-test/docker-compose.yml +57 -57
  180. package/scripts/stress-test/knex-stress-test.js +208 -208
  181. package/scripts/stress-test/mysql2-random-hanging-every-now-and-then.js +145 -145
  182. package/scripts/stress-test/mysql2-sudden-exit-without-error.js +100 -100
  183. package/scripts/stress-test/reconnect-test-mysql-based-drivers.js +184 -184
  184. package/scripts/update_gitignore_for_tsc_output.js +90 -86
  185. package/types/index.d.ts +3233 -3233
  186. package/types/result.d.ts +27 -27
  187. package/types/tables.d.ts +4 -4
@@ -1,600 +1,600 @@
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
+ '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;