knex 2.5.0 → 2.5.1

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