knex 2.4.2 → 2.5.0

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