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,1580 +1,1580 @@
1
- // Query Compiler
2
- // -------
3
- const helpers = require('../util/helpers');
4
- const Raw = require('../raw');
5
- const QueryBuilder = require('./querybuilder');
6
- const JoinClause = require('./joinclause');
7
- const debug = require('debug');
8
-
9
- const assign = require('lodash/assign');
10
- const compact = require('lodash/compact');
11
- const groupBy = require('lodash/groupBy');
12
- const has = require('lodash/has');
13
- const isEmpty = require('lodash/isEmpty');
14
- const map = require('lodash/map');
15
- const omitBy = require('lodash/omitBy');
16
- const reduce = require('lodash/reduce');
17
- const { nanoid } = require('../util/nanoid');
18
- const { isString, isUndefined } = require('../util/is');
19
- const {
20
- columnize: columnize_,
21
- direction: direction_,
22
- operator: operator_,
23
- wrap: wrap_,
24
- unwrapRaw: unwrapRaw_,
25
- rawOrFn: rawOrFn_,
26
- } = require('../formatter/wrappingFormatter');
27
-
28
- const debugBindings = debug('knex:bindings');
29
-
30
- const components = [
31
- 'columns',
32
- 'join',
33
- 'where',
34
- 'union',
35
- 'group',
36
- 'having',
37
- 'order',
38
- 'limit',
39
- 'offset',
40
- 'lock',
41
- 'waitMode',
42
- ];
43
-
44
- // The "QueryCompiler" takes all of the query statements which
45
- // have been gathered in the "QueryBuilder" and turns them into a
46
- // properly formatted / bound query string.
47
- class QueryCompiler {
48
- constructor(client, builder, bindings) {
49
- this.client = client;
50
- this.method = builder._method || 'select';
51
- this.options = builder._options;
52
- this.single = builder._single;
53
- this.timeout = builder._timeout || false;
54
- this.cancelOnTimeout = builder._cancelOnTimeout || false;
55
- this.grouped = groupBy(builder._statements, 'grouping');
56
- this.formatter = client.formatter(builder);
57
- // Used when the insert call is empty.
58
- this._emptyInsertValue = 'default values';
59
- this.first = this.select;
60
-
61
- this.bindings = bindings || [];
62
- this.formatter.bindings = this.bindings;
63
- this.bindingsHolder = this;
64
- this.builder = this.formatter.builder;
65
- }
66
-
67
- // Collapse the builder into a single object
68
- toSQL(method, tz) {
69
- this._undefinedInWhereClause = false;
70
- this.undefinedBindingsInfo = [];
71
-
72
- method = method || this.method;
73
- const val = this[method]() || '';
74
-
75
- const query = {
76
- method,
77
- options: reduce(this.options, assign, {}),
78
- timeout: this.timeout,
79
- cancelOnTimeout: this.cancelOnTimeout,
80
- bindings: this.bindingsHolder.bindings || [],
81
- __knexQueryUid: nanoid(),
82
- };
83
-
84
- Object.defineProperties(query, {
85
- toNative: {
86
- value: () => {
87
- return {
88
- sql: this.client.positionBindings(query.sql),
89
- bindings: this.client.prepBindings(query.bindings),
90
- };
91
- },
92
- enumerable: false,
93
- },
94
- });
95
-
96
- if (isString(val)) {
97
- query.sql = val;
98
- } else {
99
- assign(query, val);
100
- }
101
-
102
- if (method === 'select' || method === 'first') {
103
- if (this.single.as) {
104
- query.as = this.single.as;
105
- }
106
- }
107
-
108
- if (this._undefinedInWhereClause) {
109
- debugBindings(query.bindings);
110
- throw new Error(
111
- `Undefined binding(s) detected when compiling ` +
112
- `${method.toUpperCase()}. Undefined column(s): [${this.undefinedBindingsInfo.join(
113
- ', '
114
- )}] query: ${query.sql}`
115
- );
116
- }
117
-
118
- return query;
119
- }
120
-
121
- // Compiles the `select` statement, or nested sub-selects by calling each of
122
- // the component compilers, trimming out the empties, and returning a
123
- // generated query string.
124
- select() {
125
- let sql = this.with();
126
-
127
- let unionStatement = '';
128
-
129
- const firstStatements = [];
130
- const endStatements = [];
131
-
132
- components.forEach((component) => {
133
- const statement = this[component](this);
134
- // We store the 'union' statement to append it at the end.
135
- // We still need to call the component sequentially because of
136
- // order of bindings.
137
- switch (component) {
138
- case 'union':
139
- unionStatement = statement;
140
- break;
141
- case 'columns':
142
- case 'join':
143
- case 'where':
144
- firstStatements.push(statement);
145
- break;
146
- default:
147
- endStatements.push(statement);
148
- break;
149
- }
150
- });
151
-
152
- // Check if we need to wrap the main query.
153
- // We need to wrap main query if one of union have wrap options to true
154
- // to avoid error syntax (in PostgreSQL for example).
155
- const wrapMainQuery =
156
- this.grouped.union &&
157
- this.grouped.union.map((u) => u.wrap).some((u) => u);
158
-
159
- if (this.onlyUnions()) {
160
- const statements = compact(firstStatements.concat(endStatements)).join(
161
- ' '
162
- );
163
- sql += unionStatement + (statements ? ' ' + statements : '');
164
- } else {
165
- const allStatements =
166
- (wrapMainQuery ? '(' : '') +
167
- compact(firstStatements).join(' ') +
168
- (wrapMainQuery ? ')' : '');
169
- const endStat = compact(endStatements).join(' ');
170
- sql +=
171
- allStatements +
172
- (unionStatement ? ' ' + unionStatement : '') +
173
- (endStat ? ' ' + endStat : endStat);
174
- }
175
- return sql;
176
- }
177
-
178
- pluck() {
179
- let toPluck = this.single.pluck;
180
- if (toPluck.indexOf('.') !== -1) {
181
- toPluck = toPluck.split('.').slice(-1)[0];
182
- }
183
- return {
184
- sql: this.select(),
185
- pluck: toPluck,
186
- };
187
- }
188
-
189
- // Compiles an "insert" query, allowing for multiple
190
- // inserts using a single query statement.
191
- insert() {
192
- const insertValues = this.single.insert || [];
193
- const sql = this.with() + `insert into ${this.tableName} `;
194
- const body = this._insertBody(insertValues);
195
- return body === '' ? '' : sql + body;
196
- }
197
-
198
- _onConflictClause(columns) {
199
- return columns instanceof Raw
200
- ? this.formatter.wrap(columns)
201
- : `(${this.formatter.columnize(columns)})`;
202
- }
203
-
204
- _buildInsertValues(insertData) {
205
- let sql = '';
206
- let i = -1;
207
- while (++i < insertData.values.length) {
208
- if (i !== 0) sql += '), (';
209
- sql += this.client.parameterize(
210
- insertData.values[i],
211
- this.client.valueForUndefined,
212
- this.builder,
213
- this.bindingsHolder
214
- );
215
- }
216
- return sql;
217
- }
218
-
219
- _insertBody(insertValues) {
220
- let sql = '';
221
- if (Array.isArray(insertValues)) {
222
- if (insertValues.length === 0) {
223
- return '';
224
- }
225
- } else if (typeof insertValues === 'object' && isEmpty(insertValues)) {
226
- return sql + this._emptyInsertValue;
227
- }
228
-
229
- const insertData = this._prepInsert(insertValues);
230
- if (typeof insertData === 'string') {
231
- sql += insertData;
232
- } else {
233
- if (insertData.columns.length) {
234
- sql += `(${columnize_(
235
- insertData.columns,
236
- this.builder,
237
- this.client,
238
- this.bindingsHolder
239
- )}`;
240
- sql += ') values (' + this._buildInsertValues(insertData) + ')';
241
- } else if (insertValues.length === 1 && insertValues[0]) {
242
- sql += this._emptyInsertValue;
243
- } else {
244
- sql = '';
245
- }
246
- }
247
- return sql;
248
- }
249
-
250
- // Compiles the "update" query.
251
- update() {
252
- // Make sure tableName is processed by the formatter first.
253
- const withSQL = this.with();
254
- const { tableName } = this;
255
- const updateData = this._prepUpdate(this.single.update);
256
- const wheres = this.where();
257
- return (
258
- withSQL +
259
- `update ${this.single.only ? 'only ' : ''}${tableName}` +
260
- ' set ' +
261
- updateData.join(', ') +
262
- (wheres ? ` ${wheres}` : '')
263
- );
264
- }
265
-
266
- _hintComments() {
267
- let hints = this.grouped.hintComments || [];
268
- hints = hints.map((hint) => compact(hint.value).join(' '));
269
- hints = compact(hints).join(' ');
270
- return hints ? `/*+ ${hints} */ ` : '';
271
- }
272
-
273
- // Compiles the columns in the query, specifying if an item was distinct.
274
- columns() {
275
- let distinctClause = '';
276
- if (this.onlyUnions()) return '';
277
- const hints = this._hintComments();
278
- const columns = this.grouped.columns || [];
279
- let i = -1,
280
- sql = [];
281
- if (columns) {
282
- while (++i < columns.length) {
283
- const stmt = columns[i];
284
- if (stmt.distinct) distinctClause = 'distinct ';
285
- if (stmt.distinctOn) {
286
- distinctClause = this.distinctOn(stmt.value);
287
- continue;
288
- }
289
- if (stmt.type === 'aggregate') {
290
- sql.push(...this.aggregate(stmt));
291
- } else if (stmt.type === 'aggregateRaw') {
292
- sql.push(this.aggregateRaw(stmt));
293
- } else if (stmt.type === 'analytic') {
294
- sql.push(this.analytic(stmt));
295
- } else if (stmt.type === 'json') {
296
- sql.push(this.json(stmt));
297
- } else if (stmt.value && stmt.value.length > 0) {
298
- sql.push(
299
- columnize_(
300
- stmt.value,
301
- this.builder,
302
- this.client,
303
- this.bindingsHolder
304
- )
305
- );
306
- }
307
- }
308
- }
309
- if (sql.length === 0) sql = ['*'];
310
- const select = this.onlyJson() ? '' : 'select ';
311
- return (
312
- `${select}${hints}${distinctClause}` +
313
- sql.join(', ') +
314
- (this.tableName
315
- ? ` from ${this.single.only ? 'only ' : ''}${this.tableName}`
316
- : '')
317
- );
318
- }
319
-
320
- _aggregate(stmt, { aliasSeparator = ' as ', distinctParentheses } = {}) {
321
- const value = stmt.value;
322
- const method = stmt.method;
323
- const distinct = stmt.aggregateDistinct ? 'distinct ' : '';
324
- const wrap = (identifier) =>
325
- wrap_(
326
- identifier,
327
- undefined,
328
- this.builder,
329
- this.client,
330
- this.bindingsHolder
331
- );
332
- const addAlias = (value, alias) => {
333
- if (alias) {
334
- return value + aliasSeparator + wrap(alias);
335
- }
336
- return value;
337
- };
338
- const aggregateArray = (value, alias) => {
339
- let columns = value.map(wrap).join(', ');
340
- if (distinct) {
341
- const openParen = distinctParentheses ? '(' : ' ';
342
- const closeParen = distinctParentheses ? ')' : '';
343
- columns = distinct.trim() + openParen + columns + closeParen;
344
- }
345
- const aggregated = `${method}(${columns})`;
346
- return addAlias(aggregated, alias);
347
- };
348
- const aggregateString = (value, alias) => {
349
- const aggregated = `${method}(${distinct + wrap(value)})`;
350
- return addAlias(aggregated, alias);
351
- };
352
-
353
- if (Array.isArray(value)) {
354
- return [aggregateArray(value)];
355
- }
356
-
357
- if (typeof value === 'object') {
358
- if (stmt.alias) {
359
- throw new Error('When using an object explicit alias can not be used');
360
- }
361
- return Object.entries(value).map(([alias, column]) => {
362
- if (Array.isArray(column)) {
363
- return aggregateArray(column, alias);
364
- }
365
- return aggregateString(column, alias);
366
- });
367
- }
368
-
369
- // Allows us to speciy an alias for the aggregate types.
370
- const splitOn = value.toLowerCase().indexOf(' as ');
371
- let column = value;
372
- let { alias } = stmt;
373
- if (splitOn !== -1) {
374
- column = value.slice(0, splitOn);
375
- if (alias) {
376
- throw new Error(`Found multiple aliases for same column: ${column}`);
377
- }
378
- alias = value.slice(splitOn + 4);
379
- }
380
- return [aggregateString(column, alias)];
381
- }
382
-
383
- aggregate(stmt) {
384
- return this._aggregate(stmt);
385
- }
386
-
387
- aggregateRaw(stmt) {
388
- const distinct = stmt.aggregateDistinct ? 'distinct ' : '';
389
- return `${stmt.method}(${
390
- distinct +
391
- unwrapRaw_(
392
- stmt.value,
393
- undefined,
394
- this.builder,
395
- this.client,
396
- this.bindingsHolder
397
- )
398
- })`;
399
- }
400
-
401
- _joinTable(join) {
402
- return join.schema && !(join.table instanceof Raw)
403
- ? `${join.schema}.${join.table}`
404
- : join.table;
405
- }
406
-
407
- // Compiles all each of the `join` clauses on the query,
408
- // including any nested join queries.
409
- join() {
410
- let sql = '';
411
- let i = -1;
412
- const joins = this.grouped.join;
413
- if (!joins) return '';
414
- while (++i < joins.length) {
415
- const join = joins[i];
416
- const table = this._joinTable(join);
417
- if (i > 0) sql += ' ';
418
- if (join.joinType === 'raw') {
419
- sql += unwrapRaw_(
420
- join.table,
421
- undefined,
422
- this.builder,
423
- this.client,
424
- this.bindingsHolder
425
- );
426
- } else {
427
- sql +=
428
- join.joinType +
429
- ' join ' +
430
- wrap_(
431
- table,
432
- undefined,
433
- this.builder,
434
- this.client,
435
- this.bindingsHolder
436
- );
437
- let ii = -1;
438
- while (++ii < join.clauses.length) {
439
- const clause = join.clauses[ii];
440
- if (ii > 0) {
441
- sql += ` ${clause.bool} `;
442
- } else {
443
- sql += ` ${clause.type === 'onUsing' ? 'using' : 'on'} `;
444
- }
445
- const val = this[clause.type](clause);
446
- if (val) {
447
- sql += val;
448
- }
449
- }
450
- }
451
- }
452
- return sql;
453
- }
454
-
455
- onBetween(statement) {
456
- return (
457
- wrap_(
458
- statement.column,
459
- undefined,
460
- this.builder,
461
- this.client,
462
- this.bindingsHolder
463
- ) +
464
- ' ' +
465
- this._not(statement, 'between') +
466
- ' ' +
467
- statement.value
468
- .map((value) =>
469
- this.client.parameter(value, this.builder, this.bindingsHolder)
470
- )
471
- .join(' and ')
472
- );
473
- }
474
-
475
- onNull(statement) {
476
- return (
477
- wrap_(
478
- statement.column,
479
- undefined,
480
- this.builder,
481
- this.client,
482
- this.bindingsHolder
483
- ) +
484
- ' is ' +
485
- this._not(statement, 'null')
486
- );
487
- }
488
-
489
- onExists(statement) {
490
- return (
491
- this._not(statement, 'exists') +
492
- ' (' +
493
- rawOrFn_(
494
- statement.value,
495
- undefined,
496
- this.builder,
497
- this.client,
498
- this.bindingsHolder
499
- ) +
500
- ')'
501
- );
502
- }
503
-
504
- onIn(statement) {
505
- if (Array.isArray(statement.column)) return this.multiOnIn(statement);
506
-
507
- let values;
508
- if (statement.value instanceof Raw) {
509
- values = this.client.parameter(
510
- statement.value,
511
- this.builder,
512
- this.formatter
513
- );
514
- } else {
515
- values = this.client.parameterize(
516
- statement.value,
517
- undefined,
518
- this.builder,
519
- this.bindingsHolder
520
- );
521
- }
522
-
523
- return (
524
- wrap_(
525
- statement.column,
526
- undefined,
527
- this.builder,
528
- this.client,
529
- this.bindingsHolder
530
- ) +
531
- ' ' +
532
- this._not(statement, 'in ') +
533
- this.wrap(values)
534
- );
535
- }
536
-
537
- multiOnIn(statement) {
538
- let i = -1,
539
- sql = `(${columnize_(
540
- statement.column,
541
- this.builder,
542
- this.client,
543
- this.bindingsHolder
544
- )}) `;
545
- sql += this._not(statement, 'in ') + '((';
546
- while (++i < statement.value.length) {
547
- if (i !== 0) sql += '),(';
548
- sql += this.client.parameterize(
549
- statement.value[i],
550
- undefined,
551
- this.builder,
552
- this.bindingsHolder
553
- );
554
- }
555
- return sql + '))';
556
- }
557
-
558
- // Compiles all `where` statements on the query.
559
- where() {
560
- const wheres = this.grouped.where;
561
- if (!wheres) return;
562
- const sql = [];
563
- let i = -1;
564
- while (++i < wheres.length) {
565
- const stmt = wheres[i];
566
- if (
567
- Object.prototype.hasOwnProperty.call(stmt, 'value') &&
568
- helpers.containsUndefined(stmt.value)
569
- ) {
570
- this.undefinedBindingsInfo.push(stmt.column);
571
- this._undefinedInWhereClause = true;
572
- }
573
- const val = this[stmt.type](stmt);
574
- if (val) {
575
- if (sql.length === 0) {
576
- sql[0] = 'where';
577
- } else {
578
- sql.push(stmt.bool);
579
- }
580
- sql.push(val);
581
- }
582
- }
583
- return sql.length > 1 ? sql.join(' ') : '';
584
- }
585
-
586
- group() {
587
- return this._groupsOrders('group');
588
- }
589
-
590
- order() {
591
- return this._groupsOrders('order');
592
- }
593
-
594
- // Compiles the `having` statements.
595
- having() {
596
- const havings = this.grouped.having;
597
- if (!havings) return '';
598
- const sql = ['having'];
599
- for (let i = 0, l = havings.length; i < l; i++) {
600
- const s = havings[i];
601
- const val = this[s.type](s);
602
- if (val) {
603
- if (sql.length === 0) {
604
- sql[0] = 'where';
605
- }
606
- if (sql.length > 1 || (sql.length === 1 && sql[0] !== 'having')) {
607
- sql.push(s.bool);
608
- }
609
- sql.push(val);
610
- }
611
- }
612
- return sql.length > 1 ? sql.join(' ') : '';
613
- }
614
-
615
- havingRaw(statement) {
616
- return (
617
- this._not(statement, '') +
618
- unwrapRaw_(
619
- statement.value,
620
- undefined,
621
- this.builder,
622
- this.client,
623
- this.bindingsHolder
624
- )
625
- );
626
- }
627
-
628
- havingWrapped(statement) {
629
- const val = rawOrFn_(
630
- statement.value,
631
- 'where',
632
- this.builder,
633
- this.client,
634
- this.bindingsHolder
635
- );
636
- return (val && this._not(statement, '') + '(' + val.slice(6) + ')') || '';
637
- }
638
-
639
- havingBasic(statement) {
640
- return (
641
- this._not(statement, '') +
642
- wrap_(
643
- statement.column,
644
- undefined,
645
- this.builder,
646
- this.client,
647
- this.bindingsHolder
648
- ) +
649
- ' ' +
650
- operator_(
651
- statement.operator,
652
- this.builder,
653
- this.client,
654
- this.bindingsHolder
655
- ) +
656
- ' ' +
657
- this.client.parameter(statement.value, this.builder, this.bindingsHolder)
658
- );
659
- }
660
-
661
- havingNull(statement) {
662
- return (
663
- wrap_(
664
- statement.column,
665
- undefined,
666
- this.builder,
667
- this.client,
668
- this.bindingsHolder
669
- ) +
670
- ' is ' +
671
- this._not(statement, 'null')
672
- );
673
- }
674
-
675
- havingExists(statement) {
676
- return (
677
- this._not(statement, 'exists') +
678
- ' (' +
679
- rawOrFn_(
680
- statement.value,
681
- undefined,
682
- this.builder,
683
- this.client,
684
- this.bindingsHolder
685
- ) +
686
- ')'
687
- );
688
- }
689
-
690
- havingBetween(statement) {
691
- return (
692
- wrap_(
693
- statement.column,
694
- undefined,
695
- this.builder,
696
- this.client,
697
- this.bindingsHolder
698
- ) +
699
- ' ' +
700
- this._not(statement, 'between') +
701
- ' ' +
702
- statement.value
703
- .map((value) =>
704
- this.client.parameter(value, this.builder, this.bindingsHolder)
705
- )
706
- .join(' and ')
707
- );
708
- }
709
-
710
- havingIn(statement) {
711
- if (Array.isArray(statement.column)) return this.multiHavingIn(statement);
712
- return (
713
- wrap_(
714
- statement.column,
715
- undefined,
716
- this.builder,
717
- this.client,
718
- this.bindingsHolder
719
- ) +
720
- ' ' +
721
- this._not(statement, 'in ') +
722
- this.wrap(
723
- this.client.parameterize(
724
- statement.value,
725
- undefined,
726
- this.builder,
727
- this.bindingsHolder
728
- )
729
- )
730
- );
731
- }
732
-
733
- multiHavingIn(statement) {
734
- return this.multiOnIn(statement);
735
- }
736
-
737
- // Compile the "union" queries attached to the main query.
738
- union() {
739
- const onlyUnions = this.onlyUnions();
740
- const unions = this.grouped.union;
741
- if (!unions) return '';
742
- let sql = '';
743
- for (let i = 0, l = unions.length; i < l; i++) {
744
- const union = unions[i];
745
- if (i > 0) sql += ' ';
746
- if (i > 0 || !onlyUnions) sql += union.clause + ' ';
747
- const statement = rawOrFn_(
748
- union.value,
749
- undefined,
750
- this.builder,
751
- this.client,
752
- this.bindingsHolder
753
- );
754
- if (statement) {
755
- const wrap = union.wrap;
756
- if (wrap) sql += '(';
757
- sql += statement;
758
- if (wrap) sql += ')';
759
- }
760
- }
761
- return sql;
762
- }
763
-
764
- // If we haven't specified any columns or a `tableName`, we're assuming this
765
- // is only being used for unions.
766
- onlyUnions() {
767
- return (
768
- (!this.grouped.columns || !!this.grouped.columns[0].value) &&
769
- this.grouped.union &&
770
- !this.tableName
771
- );
772
- }
773
-
774
- _getValueOrParameterFromAttribute(attribute, rawValue) {
775
- if (this.single.skipBinding[attribute] === true) {
776
- return rawValue !== undefined && rawValue !== null
777
- ? rawValue
778
- : this.single[attribute];
779
- }
780
- return this.client.parameter(
781
- this.single[attribute],
782
- this.builder,
783
- this.bindingsHolder
784
- );
785
- }
786
-
787
- onlyJson() {
788
- return (
789
- !this.tableName &&
790
- this.grouped.columns &&
791
- this.grouped.columns.length === 1 &&
792
- this.grouped.columns[0].type === 'json'
793
- );
794
- }
795
-
796
- limit() {
797
- const noLimit = !this.single.limit && this.single.limit !== 0;
798
- if (noLimit) return '';
799
- return `limit ${this._getValueOrParameterFromAttribute('limit')}`;
800
- }
801
-
802
- offset() {
803
- if (!this.single.offset) return '';
804
- return `offset ${this._getValueOrParameterFromAttribute('offset')}`;
805
- }
806
-
807
- // Compiles a `delete` query.
808
- del() {
809
- // Make sure tableName is processed by the formatter first.
810
- const { tableName } = this;
811
- const withSQL = this.with();
812
- const wheres = this.where();
813
- const joins = this.join();
814
- // When using joins, delete the "from" table values as a default
815
- const deleteSelector = joins ? tableName + ' ' : '';
816
- return (
817
- withSQL +
818
- `delete ${deleteSelector}from ${
819
- this.single.only ? 'only ' : ''
820
- }${tableName}` +
821
- (joins ? ` ${joins}` : '') +
822
- (wheres ? ` ${wheres}` : '')
823
- );
824
- }
825
-
826
- // Compiles a `truncate` query.
827
- truncate() {
828
- return `truncate ${this.tableName}`;
829
- }
830
-
831
- // Compiles the "locks".
832
- lock() {
833
- if (this.single.lock) {
834
- return this[this.single.lock]();
835
- }
836
- }
837
-
838
- // Compiles the wait mode on the locks.
839
- waitMode() {
840
- if (this.single.waitMode) {
841
- return this[this.single.waitMode]();
842
- }
843
- }
844
-
845
- // Fail on unsupported databases
846
- skipLocked() {
847
- throw new Error(
848
- '.skipLocked() is currently only supported on MySQL 8.0+ and PostgreSQL 9.5+'
849
- );
850
- }
851
-
852
- // Fail on unsupported databases
853
- noWait() {
854
- throw new Error(
855
- '.noWait() is currently only supported on MySQL 8.0+, MariaDB 10.3.0+ and PostgreSQL 9.5+'
856
- );
857
- }
858
-
859
- distinctOn(value) {
860
- throw new Error('.distinctOn() is currently only supported on PostgreSQL');
861
- }
862
-
863
- // On Clause
864
- // ------
865
-
866
- onWrapped(clause) {
867
- const self = this;
868
-
869
- const wrapJoin = new JoinClause();
870
- clause.value.call(wrapJoin, wrapJoin);
871
-
872
- let sql = '';
873
-
874
- for (let ii = 0; ii < wrapJoin.clauses.length; ii++) {
875
- const wrapClause = wrapJoin.clauses[ii];
876
- if (ii > 0) {
877
- sql += ` ${wrapClause.bool} `;
878
- }
879
- const val = self[wrapClause.type](wrapClause);
880
- if (val) {
881
- sql += val;
882
- }
883
- }
884
-
885
- if (sql.length) {
886
- return `(${sql})`;
887
- }
888
- return '';
889
- }
890
-
891
- onBasic(clause) {
892
- const toWrap = clause.value instanceof QueryBuilder;
893
- return (
894
- wrap_(
895
- clause.column,
896
- undefined,
897
- this.builder,
898
- this.client,
899
- this.bindingsHolder
900
- ) +
901
- ' ' +
902
- operator_(
903
- clause.operator,
904
- this.builder,
905
- this.client,
906
- this.bindingsHolder
907
- ) +
908
- ' ' +
909
- (toWrap ? '(' : '') +
910
- wrap_(
911
- clause.value,
912
- undefined,
913
- this.builder,
914
- this.client,
915
- this.bindingsHolder
916
- ) +
917
- (toWrap ? ')' : '')
918
- );
919
- }
920
-
921
- onVal(clause) {
922
- return (
923
- wrap_(
924
- clause.column,
925
- undefined,
926
- this.builder,
927
- this.client,
928
- this.bindingsHolder
929
- ) +
930
- ' ' +
931
- operator_(
932
- clause.operator,
933
- this.builder,
934
- this.client,
935
- this.bindingsHolder
936
- ) +
937
- ' ' +
938
- this.client.parameter(clause.value, this.builder, this.bindingsHolder)
939
- );
940
- }
941
-
942
- onRaw(clause) {
943
- return unwrapRaw_(
944
- clause.value,
945
- undefined,
946
- this.builder,
947
- this.client,
948
- this.bindingsHolder
949
- );
950
- }
951
-
952
- onUsing(clause) {
953
- return (
954
- '(' +
955
- columnize_(
956
- clause.column,
957
- this.builder,
958
- this.client,
959
- this.bindingsHolder
960
- ) +
961
- ')'
962
- );
963
- }
964
-
965
- // Where Clause
966
- // ------
967
-
968
- _valueClause(statement) {
969
- return statement.asColumn
970
- ? wrap_(
971
- statement.value,
972
- undefined,
973
- this.builder,
974
- this.client,
975
- this.bindingsHolder
976
- )
977
- : this.client.parameter(
978
- statement.value,
979
- this.builder,
980
- this.bindingsHolder
981
- );
982
- }
983
-
984
- _columnClause(statement) {
985
- let columns;
986
- if (Array.isArray(statement.column)) {
987
- columns = `(${columnize_(
988
- statement.column,
989
- this.builder,
990
- this.client,
991
- this.bindingsHolder
992
- )})`;
993
- } else {
994
- columns = wrap_(
995
- statement.column,
996
- undefined,
997
- this.builder,
998
- this.client,
999
- this.bindingsHolder
1000
- );
1001
- }
1002
- return columns;
1003
- }
1004
-
1005
- whereIn(statement) {
1006
- const values = this.client.values(
1007
- statement.value,
1008
- this.builder,
1009
- this.bindingsHolder
1010
- );
1011
- return `${this._columnClause(statement)} ${this._not(
1012
- statement,
1013
- 'in '
1014
- )}${values}`;
1015
- }
1016
-
1017
- whereLike(statement) {
1018
- return `${this._columnClause(statement)} ${this._not(
1019
- statement,
1020
- 'like '
1021
- )}${this._valueClause(statement)}`;
1022
- }
1023
-
1024
- whereILike(statement) {
1025
- return `${this._columnClause(statement)} ${this._not(
1026
- statement,
1027
- 'ilike '
1028
- )}${this._valueClause(statement)}`;
1029
- }
1030
-
1031
- whereNull(statement) {
1032
- return (
1033
- wrap_(
1034
- statement.column,
1035
- undefined,
1036
- this.builder,
1037
- this.client,
1038
- this.bindingsHolder
1039
- ) +
1040
- ' is ' +
1041
- this._not(statement, 'null')
1042
- );
1043
- }
1044
-
1045
- // Compiles a basic "where" clause.
1046
- whereBasic(statement) {
1047
- return (
1048
- this._not(statement, '') +
1049
- wrap_(
1050
- statement.column,
1051
- undefined,
1052
- this.builder,
1053
- this.client,
1054
- this.bindingsHolder
1055
- ) +
1056
- ' ' +
1057
- operator_(
1058
- statement.operator,
1059
- this.builder,
1060
- this.client,
1061
- this.bindingsHolder
1062
- ) +
1063
- ' ' +
1064
- this._valueClause(statement)
1065
- );
1066
- }
1067
-
1068
- whereExists(statement) {
1069
- return (
1070
- this._not(statement, 'exists') +
1071
- ' (' +
1072
- rawOrFn_(
1073
- statement.value,
1074
- undefined,
1075
- this.builder,
1076
- this.client,
1077
- this.bindingsHolder
1078
- ) +
1079
- ')'
1080
- );
1081
- }
1082
-
1083
- whereWrapped(statement) {
1084
- const val = rawOrFn_(
1085
- statement.value,
1086
- 'where',
1087
- this.builder,
1088
- this.client,
1089
- this.bindingsHolder
1090
- );
1091
- return (val && this._not(statement, '') + '(' + val.slice(6) + ')') || '';
1092
- }
1093
-
1094
- whereBetween(statement) {
1095
- return (
1096
- wrap_(
1097
- statement.column,
1098
- undefined,
1099
- this.builder,
1100
- this.client,
1101
- this.bindingsHolder
1102
- ) +
1103
- ' ' +
1104
- this._not(statement, 'between') +
1105
- ' ' +
1106
- statement.value
1107
- .map((value) =>
1108
- this.client.parameter(value, this.builder, this.bindingsHolder)
1109
- )
1110
- .join(' and ')
1111
- );
1112
- }
1113
-
1114
- // Compiles a "whereRaw" query.
1115
- whereRaw(statement) {
1116
- return (
1117
- this._not(statement, '') +
1118
- unwrapRaw_(
1119
- statement.value,
1120
- undefined,
1121
- this.builder,
1122
- this.client,
1123
- this.bindingsHolder
1124
- )
1125
- );
1126
- }
1127
-
1128
- _jsonWrapValue(jsonValue) {
1129
- if (!this.builder._isJsonObject(jsonValue)) {
1130
- try {
1131
- return JSON.stringify(JSON.parse(jsonValue.replace(/\n|\t/g, '')));
1132
- } catch (e) {
1133
- return jsonValue;
1134
- }
1135
- }
1136
- return JSON.stringify(jsonValue);
1137
- }
1138
-
1139
- _jsonValueClause(statement) {
1140
- statement.value = this._jsonWrapValue(statement.value);
1141
- return this._valueClause(statement);
1142
- }
1143
-
1144
- whereJsonObject(statement) {
1145
- return `${this._columnClause(statement)} ${
1146
- statement.not ? '!=' : '='
1147
- } ${this._jsonValueClause(statement)}`;
1148
- }
1149
-
1150
- wrap(str) {
1151
- if (str.charAt(0) !== '(') return `(${str})`;
1152
- return str;
1153
- }
1154
-
1155
- json(stmt) {
1156
- return this[stmt.method](stmt.params);
1157
- }
1158
-
1159
- analytic(stmt) {
1160
- let sql = '';
1161
- const self = this;
1162
- sql += stmt.method + '() over (';
1163
-
1164
- if (stmt.raw) {
1165
- sql += stmt.raw;
1166
- } else {
1167
- if (stmt.partitions.length) {
1168
- sql += 'partition by ';
1169
- sql +=
1170
- map(stmt.partitions, function (partition) {
1171
- if (isString(partition)) {
1172
- return self.formatter.columnize(partition);
1173
- } else return self.formatter.columnize(partition.column) + (partition.order ? ' ' + partition.order : '');
1174
- }).join(', ') + ' ';
1175
- }
1176
-
1177
- sql += 'order by ';
1178
- sql += map(stmt.order, function (order) {
1179
- if (isString(order)) {
1180
- return self.formatter.columnize(order);
1181
- } else return self.formatter.columnize(order.column) + (order.order ? ' ' + order.order : '');
1182
- }).join(', ');
1183
- }
1184
-
1185
- sql += ')';
1186
-
1187
- if (stmt.alias) {
1188
- sql += ' as ' + stmt.alias;
1189
- }
1190
-
1191
- return sql;
1192
- }
1193
-
1194
- // Compiles all `with` statements on the query.
1195
- with() {
1196
- if (!this.grouped.with || !this.grouped.with.length) {
1197
- return '';
1198
- }
1199
- const withs = this.grouped.with;
1200
- if (!withs) return;
1201
- const sql = [];
1202
- let i = -1;
1203
- let isRecursive = false;
1204
- while (++i < withs.length) {
1205
- const stmt = withs[i];
1206
- if (stmt.recursive) {
1207
- isRecursive = true;
1208
- }
1209
- const val = this[stmt.type](stmt);
1210
- sql.push(val);
1211
- }
1212
- return `with ${isRecursive ? 'recursive ' : ''}${sql.join(', ')} `;
1213
- }
1214
-
1215
- withWrapped(statement) {
1216
- const val = rawOrFn_(
1217
- statement.value,
1218
- undefined,
1219
- this.builder,
1220
- this.client,
1221
- this.bindingsHolder
1222
- );
1223
- const columnList = statement.columnList
1224
- ? '(' +
1225
- columnize_(
1226
- statement.columnList,
1227
- this.builder,
1228
- this.client,
1229
- this.bindingsHolder
1230
- ) +
1231
- ')'
1232
- : '';
1233
- const materialized =
1234
- statement.materialized === undefined
1235
- ? ''
1236
- : statement.materialized
1237
- ? 'materialized '
1238
- : 'not materialized ';
1239
- return (
1240
- (val &&
1241
- columnize_(
1242
- statement.alias,
1243
- this.builder,
1244
- this.client,
1245
- this.bindingsHolder
1246
- ) +
1247
- columnList +
1248
- ' as ' +
1249
- materialized +
1250
- '(' +
1251
- val +
1252
- ')') ||
1253
- ''
1254
- );
1255
- }
1256
-
1257
- // Determines whether to add a "not" prefix to the where clause.
1258
- _not(statement, str) {
1259
- if (statement.not) return `not ${str}`;
1260
- return str;
1261
- }
1262
-
1263
- _prepInsert(data) {
1264
- const isRaw = rawOrFn_(
1265
- data,
1266
- undefined,
1267
- this.builder,
1268
- this.client,
1269
- this.bindingsHolder
1270
- );
1271
- if (isRaw) return isRaw;
1272
- let columns = [];
1273
- const values = [];
1274
- if (!Array.isArray(data)) data = data ? [data] : [];
1275
- let i = -1;
1276
- while (++i < data.length) {
1277
- if (data[i] == null) break;
1278
- if (i === 0) columns = Object.keys(data[i]).sort();
1279
- const row = new Array(columns.length);
1280
- const keys = Object.keys(data[i]);
1281
- let j = -1;
1282
- while (++j < keys.length) {
1283
- const key = keys[j];
1284
- let idx = columns.indexOf(key);
1285
- if (idx === -1) {
1286
- columns = columns.concat(key).sort();
1287
- idx = columns.indexOf(key);
1288
- let k = -1;
1289
- while (++k < values.length) {
1290
- values[k].splice(idx, 0, undefined);
1291
- }
1292
- row.splice(idx, 0, undefined);
1293
- }
1294
- row[idx] = data[i][key];
1295
- }
1296
- values.push(row);
1297
- }
1298
- return {
1299
- columns,
1300
- values,
1301
- };
1302
- }
1303
-
1304
- // "Preps" the update.
1305
- _prepUpdate(data = {}) {
1306
- const { counter = {} } = this.single;
1307
-
1308
- for (const column of Object.keys(counter)) {
1309
- //Skip?
1310
- if (has(data, column)) {
1311
- //Needed?
1312
- this.client.logger.warn(
1313
- `increment/decrement called for a column that has already been specified in main .update() call. Ignoring increment/decrement and using value from .update() call.`
1314
- );
1315
- continue;
1316
- }
1317
-
1318
- let value = counter[column];
1319
-
1320
- const symbol = value < 0 ? '-' : '+';
1321
-
1322
- if (symbol === '-') {
1323
- value = -value;
1324
- }
1325
-
1326
- data[column] = this.client.raw(`?? ${symbol} ?`, [column, value]);
1327
- }
1328
-
1329
- data = omitBy(data, isUndefined);
1330
-
1331
- const vals = [];
1332
- const columns = Object.keys(data);
1333
- let i = -1;
1334
-
1335
- while (++i < columns.length) {
1336
- vals.push(
1337
- wrap_(
1338
- columns[i],
1339
- undefined,
1340
- this.builder,
1341
- this.client,
1342
- this.bindingsHolder
1343
- ) +
1344
- ' = ' +
1345
- this.client.parameter(
1346
- data[columns[i]],
1347
- this.builder,
1348
- this.bindingsHolder
1349
- )
1350
- );
1351
- }
1352
-
1353
- if (isEmpty(vals)) {
1354
- throw new Error(
1355
- [
1356
- 'Empty .update() call detected!',
1357
- 'Update data does not contain any values to update.',
1358
- 'This will result in a faulty query.',
1359
- this.single.table ? `Table: ${this.single.table}.` : '',
1360
- this.single.update
1361
- ? `Columns: ${Object.keys(this.single.update)}.`
1362
- : '',
1363
- ].join(' ')
1364
- );
1365
- }
1366
-
1367
- return vals;
1368
- }
1369
-
1370
- _formatGroupsItemValue(value, nulls) {
1371
- const { formatter } = this;
1372
- let nullOrder = '';
1373
- if (nulls === 'last') {
1374
- nullOrder = ' is null';
1375
- } else if (nulls === 'first') {
1376
- nullOrder = ' is not null';
1377
- }
1378
-
1379
- let groupOrder;
1380
- if (value instanceof Raw) {
1381
- groupOrder = unwrapRaw_(
1382
- value,
1383
- undefined,
1384
- this.builder,
1385
- this.client,
1386
- this.bindingsHolder
1387
- );
1388
- } else if (value instanceof QueryBuilder || nulls) {
1389
- groupOrder = '(' + formatter.columnize(value) + nullOrder + ')';
1390
- } else {
1391
- groupOrder = formatter.columnize(value);
1392
- }
1393
- return groupOrder;
1394
- }
1395
-
1396
- _basicGroupOrder(item, type) {
1397
- const column = this._formatGroupsItemValue(item.value, item.nulls);
1398
- const direction =
1399
- type === 'order' && item.type !== 'orderByRaw'
1400
- ? ` ${direction_(
1401
- item.direction,
1402
- this.builder,
1403
- this.client,
1404
- this.bindingsHolder
1405
- )}`
1406
- : '';
1407
- return column + direction;
1408
- }
1409
-
1410
- _groupOrder(item, type) {
1411
- return this._basicGroupOrder(item, type);
1412
- }
1413
-
1414
- _groupOrderNulls(item, type) {
1415
- const column = this._formatGroupsItemValue(item.value);
1416
- const direction =
1417
- type === 'order' && item.type !== 'orderByRaw'
1418
- ? ` ${direction_(
1419
- item.direction,
1420
- this.builder,
1421
- this.client,
1422
- this.bindingsHolder
1423
- )}`
1424
- : '';
1425
- if (item.nulls && !(item.value instanceof Raw)) {
1426
- return `${column}${direction ? direction : ''} nulls ${item.nulls}`;
1427
- }
1428
- return column + direction;
1429
- }
1430
-
1431
- // Compiles the `order by` statements.
1432
- _groupsOrders(type) {
1433
- const items = this.grouped[type];
1434
- if (!items) return '';
1435
- const sql = items.map((item) => {
1436
- return this._groupOrder(item, type);
1437
- });
1438
- return sql.length ? type + ' by ' + sql.join(', ') : '';
1439
- }
1440
-
1441
- // Get the table name, wrapping it if necessary.
1442
- // Implemented as a property to prevent ordering issues as described in #704.
1443
- get tableName() {
1444
- if (!this._tableName) {
1445
- // Only call this.formatter.wrap() the first time this property is accessed.
1446
- let tableName = this.single.table;
1447
- const schemaName = this.single.schema;
1448
-
1449
- if (tableName && schemaName) {
1450
- const isQueryBuilder = tableName instanceof QueryBuilder;
1451
- const isRawQuery = tableName instanceof Raw;
1452
- const isFunction = typeof tableName === 'function';
1453
-
1454
- if (!isQueryBuilder && !isRawQuery && !isFunction) {
1455
- tableName = `${schemaName}.${tableName}`;
1456
- }
1457
- }
1458
-
1459
- this._tableName = tableName
1460
- ? // Wrap subQuery with parenthesis, #3485
1461
- wrap_(
1462
- tableName,
1463
- tableName instanceof QueryBuilder,
1464
- this.builder,
1465
- this.client,
1466
- this.bindingsHolder
1467
- )
1468
- : '';
1469
- }
1470
- return this._tableName;
1471
- }
1472
-
1473
- _jsonPathWrap(extraction) {
1474
- return this.client.parameter(
1475
- extraction.path || extraction[1],
1476
- this.builder,
1477
- this.bindingsHolder
1478
- );
1479
- }
1480
-
1481
- // Json common functions
1482
- _jsonExtract(nameFunction, params) {
1483
- let extractions;
1484
- if (Array.isArray(params.column)) {
1485
- extractions = params.column;
1486
- } else {
1487
- extractions = [params];
1488
- }
1489
- if (!Array.isArray(nameFunction)) {
1490
- nameFunction = [nameFunction];
1491
- }
1492
- return extractions
1493
- .map((extraction) => {
1494
- let jsonCol = `${columnize_(
1495
- extraction.column || extraction[0],
1496
- this.builder,
1497
- this.client,
1498
- this.bindingsHolder
1499
- )}, ${this._jsonPathWrap(extraction)}`;
1500
- nameFunction.forEach((f) => {
1501
- jsonCol = f + '(' + jsonCol + ')';
1502
- });
1503
- const alias = extraction.alias || extraction[2];
1504
- return alias
1505
- ? this.client.alias(jsonCol, this.formatter.wrap(alias))
1506
- : jsonCol;
1507
- })
1508
- .join(', ');
1509
- }
1510
-
1511
- _jsonSet(nameFunction, params) {
1512
- const jsonSet = `${nameFunction}(${columnize_(
1513
- params.column,
1514
- this.builder,
1515
- this.client,
1516
- this.bindingsHolder
1517
- )}, ${this.client.parameter(
1518
- params.path,
1519
- this.builder,
1520
- this.bindingsHolder
1521
- )}, ${this.client.parameter(
1522
- params.value,
1523
- this.builder,
1524
- this.bindingsHolder
1525
- )})`;
1526
- return params.alias
1527
- ? this.client.alias(jsonSet, this.formatter.wrap(params.alias))
1528
- : jsonSet;
1529
- }
1530
-
1531
- _whereJsonPath(nameFunction, statement) {
1532
- return `${nameFunction}(${this._columnClause(
1533
- statement
1534
- )}, ${this._jsonPathWrap({ path: statement.jsonPath })}) ${operator_(
1535
- statement.operator,
1536
- this.builder,
1537
- this.client,
1538
- this.bindingsHolder
1539
- )} ${this._jsonValueClause(statement)}`;
1540
- }
1541
-
1542
- _onJsonPathEquals(nameJoinFunction, clause) {
1543
- return (
1544
- nameJoinFunction +
1545
- '(' +
1546
- wrap_(
1547
- clause.columnFirst,
1548
- undefined,
1549
- this.builder,
1550
- this.client,
1551
- this.bindingsHolder
1552
- ) +
1553
- ', ' +
1554
- this.client.parameter(
1555
- clause.jsonPathFirst,
1556
- this.builder,
1557
- this.bindingsHolder
1558
- ) +
1559
- ') = ' +
1560
- nameJoinFunction +
1561
- '(' +
1562
- wrap_(
1563
- clause.columnSecond,
1564
- undefined,
1565
- this.builder,
1566
- this.client,
1567
- this.bindingsHolder
1568
- ) +
1569
- ', ' +
1570
- this.client.parameter(
1571
- clause.jsonPathSecond,
1572
- this.builder,
1573
- this.bindingsHolder
1574
- ) +
1575
- ')'
1576
- );
1577
- }
1578
- }
1579
-
1580
- module.exports = QueryCompiler;
1
+ // Query Compiler
2
+ // -------
3
+ const helpers = require('../util/helpers');
4
+ const Raw = require('../raw');
5
+ const QueryBuilder = require('./querybuilder');
6
+ const JoinClause = require('./joinclause');
7
+ const debug = require('debug');
8
+
9
+ const assign = require('lodash/assign');
10
+ const compact = require('lodash/compact');
11
+ const groupBy = require('lodash/groupBy');
12
+ const has = require('lodash/has');
13
+ const isEmpty = require('lodash/isEmpty');
14
+ const map = require('lodash/map');
15
+ const omitBy = require('lodash/omitBy');
16
+ const reduce = require('lodash/reduce');
17
+ const { nanoid } = require('../util/nanoid');
18
+ const { isString, isUndefined } = require('../util/is');
19
+ const {
20
+ columnize: columnize_,
21
+ direction: direction_,
22
+ operator: operator_,
23
+ wrap: wrap_,
24
+ unwrapRaw: unwrapRaw_,
25
+ rawOrFn: rawOrFn_,
26
+ } = require('../formatter/wrappingFormatter');
27
+
28
+ const debugBindings = debug('knex:bindings');
29
+
30
+ const components = [
31
+ 'columns',
32
+ 'join',
33
+ 'where',
34
+ 'union',
35
+ 'group',
36
+ 'having',
37
+ 'order',
38
+ 'limit',
39
+ 'offset',
40
+ 'lock',
41
+ 'waitMode',
42
+ ];
43
+
44
+ // The "QueryCompiler" takes all of the query statements which
45
+ // have been gathered in the "QueryBuilder" and turns them into a
46
+ // properly formatted / bound query string.
47
+ class QueryCompiler {
48
+ constructor(client, builder, bindings) {
49
+ this.client = client;
50
+ this.method = builder._method || 'select';
51
+ this.options = builder._options;
52
+ this.single = builder._single;
53
+ this.timeout = builder._timeout || false;
54
+ this.cancelOnTimeout = builder._cancelOnTimeout || false;
55
+ this.grouped = groupBy(builder._statements, 'grouping');
56
+ this.formatter = client.formatter(builder);
57
+ // Used when the insert call is empty.
58
+ this._emptyInsertValue = 'default values';
59
+ this.first = this.select;
60
+
61
+ this.bindings = bindings || [];
62
+ this.formatter.bindings = this.bindings;
63
+ this.bindingsHolder = this;
64
+ this.builder = this.formatter.builder;
65
+ }
66
+
67
+ // Collapse the builder into a single object
68
+ toSQL(method, tz) {
69
+ this._undefinedInWhereClause = false;
70
+ this.undefinedBindingsInfo = [];
71
+
72
+ method = method || this.method;
73
+ const val = this[method]() || '';
74
+
75
+ const query = {
76
+ method,
77
+ options: reduce(this.options, assign, {}),
78
+ timeout: this.timeout,
79
+ cancelOnTimeout: this.cancelOnTimeout,
80
+ bindings: this.bindingsHolder.bindings || [],
81
+ __knexQueryUid: nanoid(),
82
+ };
83
+
84
+ Object.defineProperties(query, {
85
+ toNative: {
86
+ value: () => {
87
+ return {
88
+ sql: this.client.positionBindings(query.sql),
89
+ bindings: this.client.prepBindings(query.bindings),
90
+ };
91
+ },
92
+ enumerable: false,
93
+ },
94
+ });
95
+
96
+ if (isString(val)) {
97
+ query.sql = val;
98
+ } else {
99
+ assign(query, val);
100
+ }
101
+
102
+ if (method === 'select' || method === 'first') {
103
+ if (this.single.as) {
104
+ query.as = this.single.as;
105
+ }
106
+ }
107
+
108
+ if (this._undefinedInWhereClause) {
109
+ debugBindings(query.bindings);
110
+ throw new Error(
111
+ `Undefined binding(s) detected when compiling ` +
112
+ `${method.toUpperCase()}. Undefined column(s): [${this.undefinedBindingsInfo.join(
113
+ ', '
114
+ )}] query: ${query.sql}`
115
+ );
116
+ }
117
+
118
+ return query;
119
+ }
120
+
121
+ // Compiles the `select` statement, or nested sub-selects by calling each of
122
+ // the component compilers, trimming out the empties, and returning a
123
+ // generated query string.
124
+ select() {
125
+ let sql = this.with();
126
+
127
+ let unionStatement = '';
128
+
129
+ const firstStatements = [];
130
+ const endStatements = [];
131
+
132
+ components.forEach((component) => {
133
+ const statement = this[component](this);
134
+ // We store the 'union' statement to append it at the end.
135
+ // We still need to call the component sequentially because of
136
+ // order of bindings.
137
+ switch (component) {
138
+ case 'union':
139
+ unionStatement = statement;
140
+ break;
141
+ case 'columns':
142
+ case 'join':
143
+ case 'where':
144
+ firstStatements.push(statement);
145
+ break;
146
+ default:
147
+ endStatements.push(statement);
148
+ break;
149
+ }
150
+ });
151
+
152
+ // Check if we need to wrap the main query.
153
+ // We need to wrap main query if one of union have wrap options to true
154
+ // to avoid error syntax (in PostgreSQL for example).
155
+ const wrapMainQuery =
156
+ this.grouped.union &&
157
+ this.grouped.union.map((u) => u.wrap).some((u) => u);
158
+
159
+ if (this.onlyUnions()) {
160
+ const statements = compact(firstStatements.concat(endStatements)).join(
161
+ ' '
162
+ );
163
+ sql += unionStatement + (statements ? ' ' + statements : '');
164
+ } else {
165
+ const allStatements =
166
+ (wrapMainQuery ? '(' : '') +
167
+ compact(firstStatements).join(' ') +
168
+ (wrapMainQuery ? ')' : '');
169
+ const endStat = compact(endStatements).join(' ');
170
+ sql +=
171
+ allStatements +
172
+ (unionStatement ? ' ' + unionStatement : '') +
173
+ (endStat ? ' ' + endStat : endStat);
174
+ }
175
+ return sql;
176
+ }
177
+
178
+ pluck() {
179
+ let toPluck = this.single.pluck;
180
+ if (toPluck.indexOf('.') !== -1) {
181
+ toPluck = toPluck.split('.').slice(-1)[0];
182
+ }
183
+ return {
184
+ sql: this.select(),
185
+ pluck: toPluck,
186
+ };
187
+ }
188
+
189
+ // Compiles an "insert" query, allowing for multiple
190
+ // inserts using a single query statement.
191
+ insert() {
192
+ const insertValues = this.single.insert || [];
193
+ const sql = this.with() + `insert into ${this.tableName} `;
194
+ const body = this._insertBody(insertValues);
195
+ return body === '' ? '' : sql + body;
196
+ }
197
+
198
+ _onConflictClause(columns) {
199
+ return columns instanceof Raw
200
+ ? this.formatter.wrap(columns)
201
+ : `(${this.formatter.columnize(columns)})`;
202
+ }
203
+
204
+ _buildInsertValues(insertData) {
205
+ let sql = '';
206
+ let i = -1;
207
+ while (++i < insertData.values.length) {
208
+ if (i !== 0) sql += '), (';
209
+ sql += this.client.parameterize(
210
+ insertData.values[i],
211
+ this.client.valueForUndefined,
212
+ this.builder,
213
+ this.bindingsHolder
214
+ );
215
+ }
216
+ return sql;
217
+ }
218
+
219
+ _insertBody(insertValues) {
220
+ let sql = '';
221
+ if (Array.isArray(insertValues)) {
222
+ if (insertValues.length === 0) {
223
+ return '';
224
+ }
225
+ } else if (typeof insertValues === 'object' && isEmpty(insertValues)) {
226
+ return sql + this._emptyInsertValue;
227
+ }
228
+
229
+ const insertData = this._prepInsert(insertValues);
230
+ if (typeof insertData === 'string') {
231
+ sql += insertData;
232
+ } else {
233
+ if (insertData.columns.length) {
234
+ sql += `(${columnize_(
235
+ insertData.columns,
236
+ this.builder,
237
+ this.client,
238
+ this.bindingsHolder
239
+ )}`;
240
+ sql += ') values (' + this._buildInsertValues(insertData) + ')';
241
+ } else if (insertValues.length === 1 && insertValues[0]) {
242
+ sql += this._emptyInsertValue;
243
+ } else {
244
+ sql = '';
245
+ }
246
+ }
247
+ return sql;
248
+ }
249
+
250
+ // Compiles the "update" query.
251
+ update() {
252
+ // Make sure tableName is processed by the formatter first.
253
+ const withSQL = this.with();
254
+ const { tableName } = this;
255
+ const updateData = this._prepUpdate(this.single.update);
256
+ const wheres = this.where();
257
+ return (
258
+ withSQL +
259
+ `update ${this.single.only ? 'only ' : ''}${tableName}` +
260
+ ' set ' +
261
+ updateData.join(', ') +
262
+ (wheres ? ` ${wheres}` : '')
263
+ );
264
+ }
265
+
266
+ _hintComments() {
267
+ let hints = this.grouped.hintComments || [];
268
+ hints = hints.map((hint) => compact(hint.value).join(' '));
269
+ hints = compact(hints).join(' ');
270
+ return hints ? `/*+ ${hints} */ ` : '';
271
+ }
272
+
273
+ // Compiles the columns in the query, specifying if an item was distinct.
274
+ columns() {
275
+ let distinctClause = '';
276
+ if (this.onlyUnions()) return '';
277
+ const hints = this._hintComments();
278
+ const columns = this.grouped.columns || [];
279
+ let i = -1,
280
+ sql = [];
281
+ if (columns) {
282
+ while (++i < columns.length) {
283
+ const stmt = columns[i];
284
+ if (stmt.distinct) distinctClause = 'distinct ';
285
+ if (stmt.distinctOn) {
286
+ distinctClause = this.distinctOn(stmt.value);
287
+ continue;
288
+ }
289
+ if (stmt.type === 'aggregate') {
290
+ sql.push(...this.aggregate(stmt));
291
+ } else if (stmt.type === 'aggregateRaw') {
292
+ sql.push(this.aggregateRaw(stmt));
293
+ } else if (stmt.type === 'analytic') {
294
+ sql.push(this.analytic(stmt));
295
+ } else if (stmt.type === 'json') {
296
+ sql.push(this.json(stmt));
297
+ } else if (stmt.value && stmt.value.length > 0) {
298
+ sql.push(
299
+ columnize_(
300
+ stmt.value,
301
+ this.builder,
302
+ this.client,
303
+ this.bindingsHolder
304
+ )
305
+ );
306
+ }
307
+ }
308
+ }
309
+ if (sql.length === 0) sql = ['*'];
310
+ const select = this.onlyJson() ? '' : 'select ';
311
+ return (
312
+ `${select}${hints}${distinctClause}` +
313
+ sql.join(', ') +
314
+ (this.tableName
315
+ ? ` from ${this.single.only ? 'only ' : ''}${this.tableName}`
316
+ : '')
317
+ );
318
+ }
319
+
320
+ _aggregate(stmt, { aliasSeparator = ' as ', distinctParentheses } = {}) {
321
+ const value = stmt.value;
322
+ const method = stmt.method;
323
+ const distinct = stmt.aggregateDistinct ? 'distinct ' : '';
324
+ const wrap = (identifier) =>
325
+ wrap_(
326
+ identifier,
327
+ undefined,
328
+ this.builder,
329
+ this.client,
330
+ this.bindingsHolder
331
+ );
332
+ const addAlias = (value, alias) => {
333
+ if (alias) {
334
+ return value + aliasSeparator + wrap(alias);
335
+ }
336
+ return value;
337
+ };
338
+ const aggregateArray = (value, alias) => {
339
+ let columns = value.map(wrap).join(', ');
340
+ if (distinct) {
341
+ const openParen = distinctParentheses ? '(' : ' ';
342
+ const closeParen = distinctParentheses ? ')' : '';
343
+ columns = distinct.trim() + openParen + columns + closeParen;
344
+ }
345
+ const aggregated = `${method}(${columns})`;
346
+ return addAlias(aggregated, alias);
347
+ };
348
+ const aggregateString = (value, alias) => {
349
+ const aggregated = `${method}(${distinct + wrap(value)})`;
350
+ return addAlias(aggregated, alias);
351
+ };
352
+
353
+ if (Array.isArray(value)) {
354
+ return [aggregateArray(value)];
355
+ }
356
+
357
+ if (typeof value === 'object') {
358
+ if (stmt.alias) {
359
+ throw new Error('When using an object explicit alias can not be used');
360
+ }
361
+ return Object.entries(value).map(([alias, column]) => {
362
+ if (Array.isArray(column)) {
363
+ return aggregateArray(column, alias);
364
+ }
365
+ return aggregateString(column, alias);
366
+ });
367
+ }
368
+
369
+ // Allows us to speciy an alias for the aggregate types.
370
+ const splitOn = value.toLowerCase().indexOf(' as ');
371
+ let column = value;
372
+ let { alias } = stmt;
373
+ if (splitOn !== -1) {
374
+ column = value.slice(0, splitOn);
375
+ if (alias) {
376
+ throw new Error(`Found multiple aliases for same column: ${column}`);
377
+ }
378
+ alias = value.slice(splitOn + 4);
379
+ }
380
+ return [aggregateString(column, alias)];
381
+ }
382
+
383
+ aggregate(stmt) {
384
+ return this._aggregate(stmt);
385
+ }
386
+
387
+ aggregateRaw(stmt) {
388
+ const distinct = stmt.aggregateDistinct ? 'distinct ' : '';
389
+ return `${stmt.method}(${
390
+ distinct +
391
+ unwrapRaw_(
392
+ stmt.value,
393
+ undefined,
394
+ this.builder,
395
+ this.client,
396
+ this.bindingsHolder
397
+ )
398
+ })`;
399
+ }
400
+
401
+ _joinTable(join) {
402
+ return join.schema && !(join.table instanceof Raw)
403
+ ? `${join.schema}.${join.table}`
404
+ : join.table;
405
+ }
406
+
407
+ // Compiles all each of the `join` clauses on the query,
408
+ // including any nested join queries.
409
+ join() {
410
+ let sql = '';
411
+ let i = -1;
412
+ const joins = this.grouped.join;
413
+ if (!joins) return '';
414
+ while (++i < joins.length) {
415
+ const join = joins[i];
416
+ const table = this._joinTable(join);
417
+ if (i > 0) sql += ' ';
418
+ if (join.joinType === 'raw') {
419
+ sql += unwrapRaw_(
420
+ join.table,
421
+ undefined,
422
+ this.builder,
423
+ this.client,
424
+ this.bindingsHolder
425
+ );
426
+ } else {
427
+ sql +=
428
+ join.joinType +
429
+ ' join ' +
430
+ wrap_(
431
+ table,
432
+ undefined,
433
+ this.builder,
434
+ this.client,
435
+ this.bindingsHolder
436
+ );
437
+ let ii = -1;
438
+ while (++ii < join.clauses.length) {
439
+ const clause = join.clauses[ii];
440
+ if (ii > 0) {
441
+ sql += ` ${clause.bool} `;
442
+ } else {
443
+ sql += ` ${clause.type === 'onUsing' ? 'using' : 'on'} `;
444
+ }
445
+ const val = this[clause.type](clause);
446
+ if (val) {
447
+ sql += val;
448
+ }
449
+ }
450
+ }
451
+ }
452
+ return sql;
453
+ }
454
+
455
+ onBetween(statement) {
456
+ return (
457
+ wrap_(
458
+ statement.column,
459
+ undefined,
460
+ this.builder,
461
+ this.client,
462
+ this.bindingsHolder
463
+ ) +
464
+ ' ' +
465
+ this._not(statement, 'between') +
466
+ ' ' +
467
+ statement.value
468
+ .map((value) =>
469
+ this.client.parameter(value, this.builder, this.bindingsHolder)
470
+ )
471
+ .join(' and ')
472
+ );
473
+ }
474
+
475
+ onNull(statement) {
476
+ return (
477
+ wrap_(
478
+ statement.column,
479
+ undefined,
480
+ this.builder,
481
+ this.client,
482
+ this.bindingsHolder
483
+ ) +
484
+ ' is ' +
485
+ this._not(statement, 'null')
486
+ );
487
+ }
488
+
489
+ onExists(statement) {
490
+ return (
491
+ this._not(statement, 'exists') +
492
+ ' (' +
493
+ rawOrFn_(
494
+ statement.value,
495
+ undefined,
496
+ this.builder,
497
+ this.client,
498
+ this.bindingsHolder
499
+ ) +
500
+ ')'
501
+ );
502
+ }
503
+
504
+ onIn(statement) {
505
+ if (Array.isArray(statement.column)) return this.multiOnIn(statement);
506
+
507
+ let values;
508
+ if (statement.value instanceof Raw) {
509
+ values = this.client.parameter(
510
+ statement.value,
511
+ this.builder,
512
+ this.formatter
513
+ );
514
+ } else {
515
+ values = this.client.parameterize(
516
+ statement.value,
517
+ undefined,
518
+ this.builder,
519
+ this.bindingsHolder
520
+ );
521
+ }
522
+
523
+ return (
524
+ wrap_(
525
+ statement.column,
526
+ undefined,
527
+ this.builder,
528
+ this.client,
529
+ this.bindingsHolder
530
+ ) +
531
+ ' ' +
532
+ this._not(statement, 'in ') +
533
+ this.wrap(values)
534
+ );
535
+ }
536
+
537
+ multiOnIn(statement) {
538
+ let i = -1,
539
+ sql = `(${columnize_(
540
+ statement.column,
541
+ this.builder,
542
+ this.client,
543
+ this.bindingsHolder
544
+ )}) `;
545
+ sql += this._not(statement, 'in ') + '((';
546
+ while (++i < statement.value.length) {
547
+ if (i !== 0) sql += '),(';
548
+ sql += this.client.parameterize(
549
+ statement.value[i],
550
+ undefined,
551
+ this.builder,
552
+ this.bindingsHolder
553
+ );
554
+ }
555
+ return sql + '))';
556
+ }
557
+
558
+ // Compiles all `where` statements on the query.
559
+ where() {
560
+ const wheres = this.grouped.where;
561
+ if (!wheres) return;
562
+ const sql = [];
563
+ let i = -1;
564
+ while (++i < wheres.length) {
565
+ const stmt = wheres[i];
566
+ if (
567
+ Object.prototype.hasOwnProperty.call(stmt, 'value') &&
568
+ helpers.containsUndefined(stmt.value)
569
+ ) {
570
+ this.undefinedBindingsInfo.push(stmt.column);
571
+ this._undefinedInWhereClause = true;
572
+ }
573
+ const val = this[stmt.type](stmt);
574
+ if (val) {
575
+ if (sql.length === 0) {
576
+ sql[0] = 'where';
577
+ } else {
578
+ sql.push(stmt.bool);
579
+ }
580
+ sql.push(val);
581
+ }
582
+ }
583
+ return sql.length > 1 ? sql.join(' ') : '';
584
+ }
585
+
586
+ group() {
587
+ return this._groupsOrders('group');
588
+ }
589
+
590
+ order() {
591
+ return this._groupsOrders('order');
592
+ }
593
+
594
+ // Compiles the `having` statements.
595
+ having() {
596
+ const havings = this.grouped.having;
597
+ if (!havings) return '';
598
+ const sql = ['having'];
599
+ for (let i = 0, l = havings.length; i < l; i++) {
600
+ const s = havings[i];
601
+ const val = this[s.type](s);
602
+ if (val) {
603
+ if (sql.length === 0) {
604
+ sql[0] = 'where';
605
+ }
606
+ if (sql.length > 1 || (sql.length === 1 && sql[0] !== 'having')) {
607
+ sql.push(s.bool);
608
+ }
609
+ sql.push(val);
610
+ }
611
+ }
612
+ return sql.length > 1 ? sql.join(' ') : '';
613
+ }
614
+
615
+ havingRaw(statement) {
616
+ return (
617
+ this._not(statement, '') +
618
+ unwrapRaw_(
619
+ statement.value,
620
+ undefined,
621
+ this.builder,
622
+ this.client,
623
+ this.bindingsHolder
624
+ )
625
+ );
626
+ }
627
+
628
+ havingWrapped(statement) {
629
+ const val = rawOrFn_(
630
+ statement.value,
631
+ 'where',
632
+ this.builder,
633
+ this.client,
634
+ this.bindingsHolder
635
+ );
636
+ return (val && this._not(statement, '') + '(' + val.slice(6) + ')') || '';
637
+ }
638
+
639
+ havingBasic(statement) {
640
+ return (
641
+ this._not(statement, '') +
642
+ wrap_(
643
+ statement.column,
644
+ undefined,
645
+ this.builder,
646
+ this.client,
647
+ this.bindingsHolder
648
+ ) +
649
+ ' ' +
650
+ operator_(
651
+ statement.operator,
652
+ this.builder,
653
+ this.client,
654
+ this.bindingsHolder
655
+ ) +
656
+ ' ' +
657
+ this.client.parameter(statement.value, this.builder, this.bindingsHolder)
658
+ );
659
+ }
660
+
661
+ havingNull(statement) {
662
+ return (
663
+ wrap_(
664
+ statement.column,
665
+ undefined,
666
+ this.builder,
667
+ this.client,
668
+ this.bindingsHolder
669
+ ) +
670
+ ' is ' +
671
+ this._not(statement, 'null')
672
+ );
673
+ }
674
+
675
+ havingExists(statement) {
676
+ return (
677
+ this._not(statement, 'exists') +
678
+ ' (' +
679
+ rawOrFn_(
680
+ statement.value,
681
+ undefined,
682
+ this.builder,
683
+ this.client,
684
+ this.bindingsHolder
685
+ ) +
686
+ ')'
687
+ );
688
+ }
689
+
690
+ havingBetween(statement) {
691
+ return (
692
+ wrap_(
693
+ statement.column,
694
+ undefined,
695
+ this.builder,
696
+ this.client,
697
+ this.bindingsHolder
698
+ ) +
699
+ ' ' +
700
+ this._not(statement, 'between') +
701
+ ' ' +
702
+ statement.value
703
+ .map((value) =>
704
+ this.client.parameter(value, this.builder, this.bindingsHolder)
705
+ )
706
+ .join(' and ')
707
+ );
708
+ }
709
+
710
+ havingIn(statement) {
711
+ if (Array.isArray(statement.column)) return this.multiHavingIn(statement);
712
+ return (
713
+ wrap_(
714
+ statement.column,
715
+ undefined,
716
+ this.builder,
717
+ this.client,
718
+ this.bindingsHolder
719
+ ) +
720
+ ' ' +
721
+ this._not(statement, 'in ') +
722
+ this.wrap(
723
+ this.client.parameterize(
724
+ statement.value,
725
+ undefined,
726
+ this.builder,
727
+ this.bindingsHolder
728
+ )
729
+ )
730
+ );
731
+ }
732
+
733
+ multiHavingIn(statement) {
734
+ return this.multiOnIn(statement);
735
+ }
736
+
737
+ // Compile the "union" queries attached to the main query.
738
+ union() {
739
+ const onlyUnions = this.onlyUnions();
740
+ const unions = this.grouped.union;
741
+ if (!unions) return '';
742
+ let sql = '';
743
+ for (let i = 0, l = unions.length; i < l; i++) {
744
+ const union = unions[i];
745
+ if (i > 0) sql += ' ';
746
+ if (i > 0 || !onlyUnions) sql += union.clause + ' ';
747
+ const statement = rawOrFn_(
748
+ union.value,
749
+ undefined,
750
+ this.builder,
751
+ this.client,
752
+ this.bindingsHolder
753
+ );
754
+ if (statement) {
755
+ const wrap = union.wrap;
756
+ if (wrap) sql += '(';
757
+ sql += statement;
758
+ if (wrap) sql += ')';
759
+ }
760
+ }
761
+ return sql;
762
+ }
763
+
764
+ // If we haven't specified any columns or a `tableName`, we're assuming this
765
+ // is only being used for unions.
766
+ onlyUnions() {
767
+ return (
768
+ (!this.grouped.columns || !!this.grouped.columns[0].value) &&
769
+ this.grouped.union &&
770
+ !this.tableName
771
+ );
772
+ }
773
+
774
+ _getValueOrParameterFromAttribute(attribute, rawValue) {
775
+ if (this.single.skipBinding[attribute] === true) {
776
+ return rawValue !== undefined && rawValue !== null
777
+ ? rawValue
778
+ : this.single[attribute];
779
+ }
780
+ return this.client.parameter(
781
+ this.single[attribute],
782
+ this.builder,
783
+ this.bindingsHolder
784
+ );
785
+ }
786
+
787
+ onlyJson() {
788
+ return (
789
+ !this.tableName &&
790
+ this.grouped.columns &&
791
+ this.grouped.columns.length === 1 &&
792
+ this.grouped.columns[0].type === 'json'
793
+ );
794
+ }
795
+
796
+ limit() {
797
+ const noLimit = !this.single.limit && this.single.limit !== 0;
798
+ if (noLimit) return '';
799
+ return `limit ${this._getValueOrParameterFromAttribute('limit')}`;
800
+ }
801
+
802
+ offset() {
803
+ if (!this.single.offset) return '';
804
+ return `offset ${this._getValueOrParameterFromAttribute('offset')}`;
805
+ }
806
+
807
+ // Compiles a `delete` query.
808
+ del() {
809
+ // Make sure tableName is processed by the formatter first.
810
+ const { tableName } = this;
811
+ const withSQL = this.with();
812
+ const wheres = this.where();
813
+ const joins = this.join();
814
+ // When using joins, delete the "from" table values as a default
815
+ const deleteSelector = joins ? tableName + ' ' : '';
816
+ return (
817
+ withSQL +
818
+ `delete ${deleteSelector}from ${
819
+ this.single.only ? 'only ' : ''
820
+ }${tableName}` +
821
+ (joins ? ` ${joins}` : '') +
822
+ (wheres ? ` ${wheres}` : '')
823
+ );
824
+ }
825
+
826
+ // Compiles a `truncate` query.
827
+ truncate() {
828
+ return `truncate ${this.tableName}`;
829
+ }
830
+
831
+ // Compiles the "locks".
832
+ lock() {
833
+ if (this.single.lock) {
834
+ return this[this.single.lock]();
835
+ }
836
+ }
837
+
838
+ // Compiles the wait mode on the locks.
839
+ waitMode() {
840
+ if (this.single.waitMode) {
841
+ return this[this.single.waitMode]();
842
+ }
843
+ }
844
+
845
+ // Fail on unsupported databases
846
+ skipLocked() {
847
+ throw new Error(
848
+ '.skipLocked() is currently only supported on MySQL 8.0+ and PostgreSQL 9.5+'
849
+ );
850
+ }
851
+
852
+ // Fail on unsupported databases
853
+ noWait() {
854
+ throw new Error(
855
+ '.noWait() is currently only supported on MySQL 8.0+, MariaDB 10.3.0+ and PostgreSQL 9.5+'
856
+ );
857
+ }
858
+
859
+ distinctOn(value) {
860
+ throw new Error('.distinctOn() is currently only supported on PostgreSQL');
861
+ }
862
+
863
+ // On Clause
864
+ // ------
865
+
866
+ onWrapped(clause) {
867
+ const self = this;
868
+
869
+ const wrapJoin = new JoinClause();
870
+ clause.value.call(wrapJoin, wrapJoin);
871
+
872
+ let sql = '';
873
+
874
+ for (let ii = 0; ii < wrapJoin.clauses.length; ii++) {
875
+ const wrapClause = wrapJoin.clauses[ii];
876
+ if (ii > 0) {
877
+ sql += ` ${wrapClause.bool} `;
878
+ }
879
+ const val = self[wrapClause.type](wrapClause);
880
+ if (val) {
881
+ sql += val;
882
+ }
883
+ }
884
+
885
+ if (sql.length) {
886
+ return `(${sql})`;
887
+ }
888
+ return '';
889
+ }
890
+
891
+ onBasic(clause) {
892
+ const toWrap = clause.value instanceof QueryBuilder;
893
+ return (
894
+ wrap_(
895
+ clause.column,
896
+ undefined,
897
+ this.builder,
898
+ this.client,
899
+ this.bindingsHolder
900
+ ) +
901
+ ' ' +
902
+ operator_(
903
+ clause.operator,
904
+ this.builder,
905
+ this.client,
906
+ this.bindingsHolder
907
+ ) +
908
+ ' ' +
909
+ (toWrap ? '(' : '') +
910
+ wrap_(
911
+ clause.value,
912
+ undefined,
913
+ this.builder,
914
+ this.client,
915
+ this.bindingsHolder
916
+ ) +
917
+ (toWrap ? ')' : '')
918
+ );
919
+ }
920
+
921
+ onVal(clause) {
922
+ return (
923
+ wrap_(
924
+ clause.column,
925
+ undefined,
926
+ this.builder,
927
+ this.client,
928
+ this.bindingsHolder
929
+ ) +
930
+ ' ' +
931
+ operator_(
932
+ clause.operator,
933
+ this.builder,
934
+ this.client,
935
+ this.bindingsHolder
936
+ ) +
937
+ ' ' +
938
+ this.client.parameter(clause.value, this.builder, this.bindingsHolder)
939
+ );
940
+ }
941
+
942
+ onRaw(clause) {
943
+ return unwrapRaw_(
944
+ clause.value,
945
+ undefined,
946
+ this.builder,
947
+ this.client,
948
+ this.bindingsHolder
949
+ );
950
+ }
951
+
952
+ onUsing(clause) {
953
+ return (
954
+ '(' +
955
+ columnize_(
956
+ clause.column,
957
+ this.builder,
958
+ this.client,
959
+ this.bindingsHolder
960
+ ) +
961
+ ')'
962
+ );
963
+ }
964
+
965
+ // Where Clause
966
+ // ------
967
+
968
+ _valueClause(statement) {
969
+ return statement.asColumn
970
+ ? wrap_(
971
+ statement.value,
972
+ undefined,
973
+ this.builder,
974
+ this.client,
975
+ this.bindingsHolder
976
+ )
977
+ : this.client.parameter(
978
+ statement.value,
979
+ this.builder,
980
+ this.bindingsHolder
981
+ );
982
+ }
983
+
984
+ _columnClause(statement) {
985
+ let columns;
986
+ if (Array.isArray(statement.column)) {
987
+ columns = `(${columnize_(
988
+ statement.column,
989
+ this.builder,
990
+ this.client,
991
+ this.bindingsHolder
992
+ )})`;
993
+ } else {
994
+ columns = wrap_(
995
+ statement.column,
996
+ undefined,
997
+ this.builder,
998
+ this.client,
999
+ this.bindingsHolder
1000
+ );
1001
+ }
1002
+ return columns;
1003
+ }
1004
+
1005
+ whereIn(statement) {
1006
+ const values = this.client.values(
1007
+ statement.value,
1008
+ this.builder,
1009
+ this.bindingsHolder
1010
+ );
1011
+ return `${this._columnClause(statement)} ${this._not(
1012
+ statement,
1013
+ 'in '
1014
+ )}${values}`;
1015
+ }
1016
+
1017
+ whereLike(statement) {
1018
+ return `${this._columnClause(statement)} ${this._not(
1019
+ statement,
1020
+ 'like '
1021
+ )}${this._valueClause(statement)}`;
1022
+ }
1023
+
1024
+ whereILike(statement) {
1025
+ return `${this._columnClause(statement)} ${this._not(
1026
+ statement,
1027
+ 'ilike '
1028
+ )}${this._valueClause(statement)}`;
1029
+ }
1030
+
1031
+ whereNull(statement) {
1032
+ return (
1033
+ wrap_(
1034
+ statement.column,
1035
+ undefined,
1036
+ this.builder,
1037
+ this.client,
1038
+ this.bindingsHolder
1039
+ ) +
1040
+ ' is ' +
1041
+ this._not(statement, 'null')
1042
+ );
1043
+ }
1044
+
1045
+ // Compiles a basic "where" clause.
1046
+ whereBasic(statement) {
1047
+ return (
1048
+ this._not(statement, '') +
1049
+ wrap_(
1050
+ statement.column,
1051
+ undefined,
1052
+ this.builder,
1053
+ this.client,
1054
+ this.bindingsHolder
1055
+ ) +
1056
+ ' ' +
1057
+ operator_(
1058
+ statement.operator,
1059
+ this.builder,
1060
+ this.client,
1061
+ this.bindingsHolder
1062
+ ) +
1063
+ ' ' +
1064
+ this._valueClause(statement)
1065
+ );
1066
+ }
1067
+
1068
+ whereExists(statement) {
1069
+ return (
1070
+ this._not(statement, 'exists') +
1071
+ ' (' +
1072
+ rawOrFn_(
1073
+ statement.value,
1074
+ undefined,
1075
+ this.builder,
1076
+ this.client,
1077
+ this.bindingsHolder
1078
+ ) +
1079
+ ')'
1080
+ );
1081
+ }
1082
+
1083
+ whereWrapped(statement) {
1084
+ const val = rawOrFn_(
1085
+ statement.value,
1086
+ 'where',
1087
+ this.builder,
1088
+ this.client,
1089
+ this.bindingsHolder
1090
+ );
1091
+ return (val && this._not(statement, '') + '(' + val.slice(6) + ')') || '';
1092
+ }
1093
+
1094
+ whereBetween(statement) {
1095
+ return (
1096
+ wrap_(
1097
+ statement.column,
1098
+ undefined,
1099
+ this.builder,
1100
+ this.client,
1101
+ this.bindingsHolder
1102
+ ) +
1103
+ ' ' +
1104
+ this._not(statement, 'between') +
1105
+ ' ' +
1106
+ statement.value
1107
+ .map((value) =>
1108
+ this.client.parameter(value, this.builder, this.bindingsHolder)
1109
+ )
1110
+ .join(' and ')
1111
+ );
1112
+ }
1113
+
1114
+ // Compiles a "whereRaw" query.
1115
+ whereRaw(statement) {
1116
+ return (
1117
+ this._not(statement, '') +
1118
+ unwrapRaw_(
1119
+ statement.value,
1120
+ undefined,
1121
+ this.builder,
1122
+ this.client,
1123
+ this.bindingsHolder
1124
+ )
1125
+ );
1126
+ }
1127
+
1128
+ _jsonWrapValue(jsonValue) {
1129
+ if (!this.builder._isJsonObject(jsonValue)) {
1130
+ try {
1131
+ return JSON.stringify(JSON.parse(jsonValue.replace(/\n|\t/g, '')));
1132
+ } catch (e) {
1133
+ return jsonValue;
1134
+ }
1135
+ }
1136
+ return JSON.stringify(jsonValue);
1137
+ }
1138
+
1139
+ _jsonValueClause(statement) {
1140
+ statement.value = this._jsonWrapValue(statement.value);
1141
+ return this._valueClause(statement);
1142
+ }
1143
+
1144
+ whereJsonObject(statement) {
1145
+ return `${this._columnClause(statement)} ${
1146
+ statement.not ? '!=' : '='
1147
+ } ${this._jsonValueClause(statement)}`;
1148
+ }
1149
+
1150
+ wrap(str) {
1151
+ if (str.charAt(0) !== '(') return `(${str})`;
1152
+ return str;
1153
+ }
1154
+
1155
+ json(stmt) {
1156
+ return this[stmt.method](stmt.params);
1157
+ }
1158
+
1159
+ analytic(stmt) {
1160
+ let sql = '';
1161
+ const self = this;
1162
+ sql += stmt.method + '() over (';
1163
+
1164
+ if (stmt.raw) {
1165
+ sql += stmt.raw;
1166
+ } else {
1167
+ if (stmt.partitions.length) {
1168
+ sql += 'partition by ';
1169
+ sql +=
1170
+ map(stmt.partitions, function (partition) {
1171
+ if (isString(partition)) {
1172
+ return self.formatter.columnize(partition);
1173
+ } else return self.formatter.columnize(partition.column) + (partition.order ? ' ' + partition.order : '');
1174
+ }).join(', ') + ' ';
1175
+ }
1176
+
1177
+ sql += 'order by ';
1178
+ sql += map(stmt.order, function (order) {
1179
+ if (isString(order)) {
1180
+ return self.formatter.columnize(order);
1181
+ } else return self.formatter.columnize(order.column) + (order.order ? ' ' + order.order : '');
1182
+ }).join(', ');
1183
+ }
1184
+
1185
+ sql += ')';
1186
+
1187
+ if (stmt.alias) {
1188
+ sql += ' as ' + stmt.alias;
1189
+ }
1190
+
1191
+ return sql;
1192
+ }
1193
+
1194
+ // Compiles all `with` statements on the query.
1195
+ with() {
1196
+ if (!this.grouped.with || !this.grouped.with.length) {
1197
+ return '';
1198
+ }
1199
+ const withs = this.grouped.with;
1200
+ if (!withs) return;
1201
+ const sql = [];
1202
+ let i = -1;
1203
+ let isRecursive = false;
1204
+ while (++i < withs.length) {
1205
+ const stmt = withs[i];
1206
+ if (stmt.recursive) {
1207
+ isRecursive = true;
1208
+ }
1209
+ const val = this[stmt.type](stmt);
1210
+ sql.push(val);
1211
+ }
1212
+ return `with ${isRecursive ? 'recursive ' : ''}${sql.join(', ')} `;
1213
+ }
1214
+
1215
+ withWrapped(statement) {
1216
+ const val = rawOrFn_(
1217
+ statement.value,
1218
+ undefined,
1219
+ this.builder,
1220
+ this.client,
1221
+ this.bindingsHolder
1222
+ );
1223
+ const columnList = statement.columnList
1224
+ ? '(' +
1225
+ columnize_(
1226
+ statement.columnList,
1227
+ this.builder,
1228
+ this.client,
1229
+ this.bindingsHolder
1230
+ ) +
1231
+ ')'
1232
+ : '';
1233
+ const materialized =
1234
+ statement.materialized === undefined
1235
+ ? ''
1236
+ : statement.materialized
1237
+ ? 'materialized '
1238
+ : 'not materialized ';
1239
+ return (
1240
+ (val &&
1241
+ columnize_(
1242
+ statement.alias,
1243
+ this.builder,
1244
+ this.client,
1245
+ this.bindingsHolder
1246
+ ) +
1247
+ columnList +
1248
+ ' as ' +
1249
+ materialized +
1250
+ '(' +
1251
+ val +
1252
+ ')') ||
1253
+ ''
1254
+ );
1255
+ }
1256
+
1257
+ // Determines whether to add a "not" prefix to the where clause.
1258
+ _not(statement, str) {
1259
+ if (statement.not) return `not ${str}`;
1260
+ return str;
1261
+ }
1262
+
1263
+ _prepInsert(data) {
1264
+ const isRaw = rawOrFn_(
1265
+ data,
1266
+ undefined,
1267
+ this.builder,
1268
+ this.client,
1269
+ this.bindingsHolder
1270
+ );
1271
+ if (isRaw) return isRaw;
1272
+ let columns = [];
1273
+ const values = [];
1274
+ if (!Array.isArray(data)) data = data ? [data] : [];
1275
+ let i = -1;
1276
+ while (++i < data.length) {
1277
+ if (data[i] == null) break;
1278
+ if (i === 0) columns = Object.keys(data[i]).sort();
1279
+ const row = new Array(columns.length);
1280
+ const keys = Object.keys(data[i]);
1281
+ let j = -1;
1282
+ while (++j < keys.length) {
1283
+ const key = keys[j];
1284
+ let idx = columns.indexOf(key);
1285
+ if (idx === -1) {
1286
+ columns = columns.concat(key).sort();
1287
+ idx = columns.indexOf(key);
1288
+ let k = -1;
1289
+ while (++k < values.length) {
1290
+ values[k].splice(idx, 0, undefined);
1291
+ }
1292
+ row.splice(idx, 0, undefined);
1293
+ }
1294
+ row[idx] = data[i][key];
1295
+ }
1296
+ values.push(row);
1297
+ }
1298
+ return {
1299
+ columns,
1300
+ values,
1301
+ };
1302
+ }
1303
+
1304
+ // "Preps" the update.
1305
+ _prepUpdate(data = {}) {
1306
+ const { counter = {} } = this.single;
1307
+
1308
+ for (const column of Object.keys(counter)) {
1309
+ //Skip?
1310
+ if (has(data, column)) {
1311
+ //Needed?
1312
+ this.client.logger.warn(
1313
+ `increment/decrement called for a column that has already been specified in main .update() call. Ignoring increment/decrement and using value from .update() call.`
1314
+ );
1315
+ continue;
1316
+ }
1317
+
1318
+ let value = counter[column];
1319
+
1320
+ const symbol = value < 0 ? '-' : '+';
1321
+
1322
+ if (symbol === '-') {
1323
+ value = -value;
1324
+ }
1325
+
1326
+ data[column] = this.client.raw(`?? ${symbol} ?`, [column, value]);
1327
+ }
1328
+
1329
+ data = omitBy(data, isUndefined);
1330
+
1331
+ const vals = [];
1332
+ const columns = Object.keys(data);
1333
+ let i = -1;
1334
+
1335
+ while (++i < columns.length) {
1336
+ vals.push(
1337
+ wrap_(
1338
+ columns[i],
1339
+ undefined,
1340
+ this.builder,
1341
+ this.client,
1342
+ this.bindingsHolder
1343
+ ) +
1344
+ ' = ' +
1345
+ this.client.parameter(
1346
+ data[columns[i]],
1347
+ this.builder,
1348
+ this.bindingsHolder
1349
+ )
1350
+ );
1351
+ }
1352
+
1353
+ if (isEmpty(vals)) {
1354
+ throw new Error(
1355
+ [
1356
+ 'Empty .update() call detected!',
1357
+ 'Update data does not contain any values to update.',
1358
+ 'This will result in a faulty query.',
1359
+ this.single.table ? `Table: ${this.single.table}.` : '',
1360
+ this.single.update
1361
+ ? `Columns: ${Object.keys(this.single.update)}.`
1362
+ : '',
1363
+ ].join(' ')
1364
+ );
1365
+ }
1366
+
1367
+ return vals;
1368
+ }
1369
+
1370
+ _formatGroupsItemValue(value, nulls) {
1371
+ const { formatter } = this;
1372
+ let nullOrder = '';
1373
+ if (nulls === 'last') {
1374
+ nullOrder = ' is null';
1375
+ } else if (nulls === 'first') {
1376
+ nullOrder = ' is not null';
1377
+ }
1378
+
1379
+ let groupOrder;
1380
+ if (value instanceof Raw) {
1381
+ groupOrder = unwrapRaw_(
1382
+ value,
1383
+ undefined,
1384
+ this.builder,
1385
+ this.client,
1386
+ this.bindingsHolder
1387
+ );
1388
+ } else if (value instanceof QueryBuilder || nulls) {
1389
+ groupOrder = '(' + formatter.columnize(value) + nullOrder + ')';
1390
+ } else {
1391
+ groupOrder = formatter.columnize(value);
1392
+ }
1393
+ return groupOrder;
1394
+ }
1395
+
1396
+ _basicGroupOrder(item, type) {
1397
+ const column = this._formatGroupsItemValue(item.value, item.nulls);
1398
+ const direction =
1399
+ type === 'order' && item.type !== 'orderByRaw'
1400
+ ? ` ${direction_(
1401
+ item.direction,
1402
+ this.builder,
1403
+ this.client,
1404
+ this.bindingsHolder
1405
+ )}`
1406
+ : '';
1407
+ return column + direction;
1408
+ }
1409
+
1410
+ _groupOrder(item, type) {
1411
+ return this._basicGroupOrder(item, type);
1412
+ }
1413
+
1414
+ _groupOrderNulls(item, type) {
1415
+ const column = this._formatGroupsItemValue(item.value);
1416
+ const direction =
1417
+ type === 'order' && item.type !== 'orderByRaw'
1418
+ ? ` ${direction_(
1419
+ item.direction,
1420
+ this.builder,
1421
+ this.client,
1422
+ this.bindingsHolder
1423
+ )}`
1424
+ : '';
1425
+ if (item.nulls && !(item.value instanceof Raw)) {
1426
+ return `${column}${direction ? direction : ''} nulls ${item.nulls}`;
1427
+ }
1428
+ return column + direction;
1429
+ }
1430
+
1431
+ // Compiles the `order by` statements.
1432
+ _groupsOrders(type) {
1433
+ const items = this.grouped[type];
1434
+ if (!items) return '';
1435
+ const sql = items.map((item) => {
1436
+ return this._groupOrder(item, type);
1437
+ });
1438
+ return sql.length ? type + ' by ' + sql.join(', ') : '';
1439
+ }
1440
+
1441
+ // Get the table name, wrapping it if necessary.
1442
+ // Implemented as a property to prevent ordering issues as described in #704.
1443
+ get tableName() {
1444
+ if (!this._tableName) {
1445
+ // Only call this.formatter.wrap() the first time this property is accessed.
1446
+ let tableName = this.single.table;
1447
+ const schemaName = this.single.schema;
1448
+
1449
+ if (tableName && schemaName) {
1450
+ const isQueryBuilder = tableName instanceof QueryBuilder;
1451
+ const isRawQuery = tableName instanceof Raw;
1452
+ const isFunction = typeof tableName === 'function';
1453
+
1454
+ if (!isQueryBuilder && !isRawQuery && !isFunction) {
1455
+ tableName = `${schemaName}.${tableName}`;
1456
+ }
1457
+ }
1458
+
1459
+ this._tableName = tableName
1460
+ ? // Wrap subQuery with parenthesis, #3485
1461
+ wrap_(
1462
+ tableName,
1463
+ tableName instanceof QueryBuilder,
1464
+ this.builder,
1465
+ this.client,
1466
+ this.bindingsHolder
1467
+ )
1468
+ : '';
1469
+ }
1470
+ return this._tableName;
1471
+ }
1472
+
1473
+ _jsonPathWrap(extraction) {
1474
+ return this.client.parameter(
1475
+ extraction.path || extraction[1],
1476
+ this.builder,
1477
+ this.bindingsHolder
1478
+ );
1479
+ }
1480
+
1481
+ // Json common functions
1482
+ _jsonExtract(nameFunction, params) {
1483
+ let extractions;
1484
+ if (Array.isArray(params.column)) {
1485
+ extractions = params.column;
1486
+ } else {
1487
+ extractions = [params];
1488
+ }
1489
+ if (!Array.isArray(nameFunction)) {
1490
+ nameFunction = [nameFunction];
1491
+ }
1492
+ return extractions
1493
+ .map((extraction) => {
1494
+ let jsonCol = `${columnize_(
1495
+ extraction.column || extraction[0],
1496
+ this.builder,
1497
+ this.client,
1498
+ this.bindingsHolder
1499
+ )}, ${this._jsonPathWrap(extraction)}`;
1500
+ nameFunction.forEach((f) => {
1501
+ jsonCol = f + '(' + jsonCol + ')';
1502
+ });
1503
+ const alias = extraction.alias || extraction[2];
1504
+ return alias
1505
+ ? this.client.alias(jsonCol, this.formatter.wrap(alias))
1506
+ : jsonCol;
1507
+ })
1508
+ .join(', ');
1509
+ }
1510
+
1511
+ _jsonSet(nameFunction, params) {
1512
+ const jsonSet = `${nameFunction}(${columnize_(
1513
+ params.column,
1514
+ this.builder,
1515
+ this.client,
1516
+ this.bindingsHolder
1517
+ )}, ${this.client.parameter(
1518
+ params.path,
1519
+ this.builder,
1520
+ this.bindingsHolder
1521
+ )}, ${this.client.parameter(
1522
+ params.value,
1523
+ this.builder,
1524
+ this.bindingsHolder
1525
+ )})`;
1526
+ return params.alias
1527
+ ? this.client.alias(jsonSet, this.formatter.wrap(params.alias))
1528
+ : jsonSet;
1529
+ }
1530
+
1531
+ _whereJsonPath(nameFunction, statement) {
1532
+ return `${nameFunction}(${this._columnClause(
1533
+ statement
1534
+ )}, ${this._jsonPathWrap({ path: statement.jsonPath })}) ${operator_(
1535
+ statement.operator,
1536
+ this.builder,
1537
+ this.client,
1538
+ this.bindingsHolder
1539
+ )} ${this._jsonValueClause(statement)}`;
1540
+ }
1541
+
1542
+ _onJsonPathEquals(nameJoinFunction, clause) {
1543
+ return (
1544
+ nameJoinFunction +
1545
+ '(' +
1546
+ wrap_(
1547
+ clause.columnFirst,
1548
+ undefined,
1549
+ this.builder,
1550
+ this.client,
1551
+ this.bindingsHolder
1552
+ ) +
1553
+ ', ' +
1554
+ this.client.parameter(
1555
+ clause.jsonPathFirst,
1556
+ this.builder,
1557
+ this.bindingsHolder
1558
+ ) +
1559
+ ') = ' +
1560
+ nameJoinFunction +
1561
+ '(' +
1562
+ wrap_(
1563
+ clause.columnSecond,
1564
+ undefined,
1565
+ this.builder,
1566
+ this.client,
1567
+ this.bindingsHolder
1568
+ ) +
1569
+ ', ' +
1570
+ this.client.parameter(
1571
+ clause.jsonPathSecond,
1572
+ this.builder,
1573
+ this.bindingsHolder
1574
+ ) +
1575
+ ')'
1576
+ );
1577
+ }
1578
+ }
1579
+
1580
+ module.exports = QueryCompiler;