knex 3.1.0 → 3.2.1

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