knex 0.21.20 → 0.21.21

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 (141) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/CONTRIBUTING.md +184 -184
  3. package/LICENSE +22 -22
  4. package/README.md +95 -95
  5. package/bin/cli.js +414 -414
  6. package/bin/utils/cli-config-utils.js +151 -151
  7. package/bin/utils/constants.js +7 -7
  8. package/bin/utils/migrationsLister.js +37 -37
  9. package/knex.js +8 -8
  10. package/lib/client.js +413 -413
  11. package/lib/config-resolver.js +61 -61
  12. package/lib/constants.js +44 -44
  13. package/lib/dialects/mssql/index.js +390 -390
  14. package/lib/dialects/mssql/query/compiler.js +444 -444
  15. package/lib/dialects/mssql/schema/columncompiler.js +103 -103
  16. package/lib/dialects/mssql/schema/compiler.js +59 -59
  17. package/lib/dialects/mssql/schema/tablecompiler.js +245 -245
  18. package/lib/dialects/mssql/transaction.js +97 -97
  19. package/lib/dialects/mysql/index.js +191 -191
  20. package/lib/dialects/mysql/query/compiler.js +142 -142
  21. package/lib/dialects/mysql/schema/columncompiler.js +171 -171
  22. package/lib/dialects/mysql/schema/compiler.js +60 -60
  23. package/lib/dialects/mysql/schema/tablecompiler.js +262 -262
  24. package/lib/dialects/mysql/transaction.js +48 -48
  25. package/lib/dialects/mysql2/index.js +35 -35
  26. package/lib/dialects/mysql2/transaction.js +46 -46
  27. package/lib/dialects/oracle/DEAD_CODE.md +5 -5
  28. package/lib/dialects/oracle/formatter.js +20 -20
  29. package/lib/dialects/oracle/index.js +79 -79
  30. package/lib/dialects/oracle/query/compiler.js +327 -327
  31. package/lib/dialects/oracle/schema/columnbuilder.js +18 -18
  32. package/lib/dialects/oracle/schema/columncompiler.js +139 -139
  33. package/lib/dialects/oracle/schema/compiler.js +81 -81
  34. package/lib/dialects/oracle/schema/tablecompiler.js +165 -165
  35. package/lib/dialects/oracle/schema/trigger.js +126 -126
  36. package/lib/dialects/oracle/utils.js +86 -86
  37. package/lib/dialects/oracledb/index.js +489 -489
  38. package/lib/dialects/oracledb/query/compiler.js +363 -363
  39. package/lib/dialects/oracledb/schema/columncompiler.js +35 -35
  40. package/lib/dialects/oracledb/transaction.js +76 -76
  41. package/lib/dialects/oracledb/utils.js +14 -14
  42. package/lib/dialects/postgres/index.js +319 -319
  43. package/lib/dialects/postgres/query/compiler.js +206 -206
  44. package/lib/dialects/postgres/schema/columncompiler.js +125 -125
  45. package/lib/dialects/postgres/schema/compiler.js +109 -109
  46. package/lib/dialects/postgres/schema/tablecompiler.js +183 -183
  47. package/lib/dialects/redshift/index.js +73 -73
  48. package/lib/dialects/redshift/query/compiler.js +119 -119
  49. package/lib/dialects/redshift/schema/columnbuilder.js +20 -20
  50. package/lib/dialects/redshift/schema/columncompiler.js +60 -60
  51. package/lib/dialects/redshift/schema/compiler.js +14 -14
  52. package/lib/dialects/redshift/schema/tablecompiler.js +123 -123
  53. package/lib/dialects/redshift/transaction.js +18 -18
  54. package/lib/dialects/sqlite3/formatter.js +21 -21
  55. package/lib/dialects/sqlite3/index.js +169 -169
  56. package/lib/dialects/sqlite3/query/compiler.js +222 -222
  57. package/lib/dialects/sqlite3/schema/columncompiler.js +27 -27
  58. package/lib/dialects/sqlite3/schema/compiler.js +49 -49
  59. package/lib/dialects/sqlite3/schema/ddl.js +525 -525
  60. package/lib/dialects/sqlite3/schema/tablecompiler.js +238 -238
  61. package/lib/formatter.js +295 -295
  62. package/lib/functionhelper.js +14 -14
  63. package/lib/helpers.js +92 -92
  64. package/lib/index.js +3 -3
  65. package/lib/interface.js +115 -115
  66. package/lib/knex.js +42 -42
  67. package/lib/logger.js +76 -76
  68. package/lib/migrate/MigrationGenerator.js +82 -82
  69. package/lib/migrate/Migrator.js +611 -611
  70. package/lib/migrate/configuration-merger.js +60 -60
  71. package/lib/migrate/migrate-stub.js +17 -17
  72. package/lib/migrate/migration-list-resolver.js +36 -36
  73. package/lib/migrate/sources/fs-migrations.js +99 -99
  74. package/lib/migrate/stub/cjs.stub +15 -15
  75. package/lib/migrate/stub/coffee.stub +13 -13
  76. package/lib/migrate/stub/eg.stub +14 -14
  77. package/lib/migrate/stub/js.stub +15 -15
  78. package/lib/migrate/stub/knexfile-coffee.stub +34 -34
  79. package/lib/migrate/stub/knexfile-eg.stub +43 -43
  80. package/lib/migrate/stub/knexfile-js.stub +44 -44
  81. package/lib/migrate/stub/knexfile-ls.stub +35 -35
  82. package/lib/migrate/stub/knexfile-ts.stub +44 -44
  83. package/lib/migrate/stub/ls.stub +14 -14
  84. package/lib/migrate/stub/ts.stub +21 -21
  85. package/lib/migrate/table-creator.js +67 -67
  86. package/lib/migrate/table-resolver.js +27 -27
  87. package/lib/query/builder.js +1372 -1372
  88. package/lib/query/compiler.js +889 -889
  89. package/lib/query/constants.js +13 -13
  90. package/lib/query/joinclause.js +263 -263
  91. package/lib/query/methods.js +92 -92
  92. package/lib/query/string.js +190 -190
  93. package/lib/raw.js +188 -188
  94. package/lib/ref.js +39 -39
  95. package/lib/runner.js +285 -285
  96. package/lib/schema/builder.js +82 -82
  97. package/lib/schema/columnbuilder.js +117 -117
  98. package/lib/schema/columncompiler.js +177 -177
  99. package/lib/schema/compiler.js +101 -101
  100. package/lib/schema/helpers.js +51 -51
  101. package/lib/schema/tablebuilder.js +288 -288
  102. package/lib/schema/tablecompiler.js +296 -296
  103. package/lib/seed/Seeder.js +203 -203
  104. package/lib/seed/seed-stub.js +13 -13
  105. package/lib/seed/stub/coffee.stub +9 -9
  106. package/lib/seed/stub/eg.stub +11 -11
  107. package/lib/seed/stub/js.stub +13 -13
  108. package/lib/seed/stub/ls.stub +11 -11
  109. package/lib/seed/stub/ts.stub +13 -13
  110. package/lib/transaction.js +363 -363
  111. package/lib/util/batchInsert.js +59 -59
  112. package/lib/util/delay.js +6 -6
  113. package/lib/util/fake-client.js +9 -9
  114. package/lib/util/finally-mixin.js +13 -13
  115. package/lib/util/fs.js +76 -76
  116. package/lib/util/import-file.js +13 -13
  117. package/lib/util/is-module-type.js +14 -14
  118. package/lib/util/is.js +32 -32
  119. package/lib/util/make-knex.js +338 -338
  120. package/lib/util/nanoid.js +29 -29
  121. package/lib/util/noop.js +1 -1
  122. package/lib/util/parse-connection.js +66 -66
  123. package/lib/util/save-async-stack.js +14 -14
  124. package/lib/util/template.js +52 -52
  125. package/lib/util/timeout.js +29 -29
  126. package/lib/util/timestamp.js +16 -16
  127. package/package.json +1 -1
  128. package/scripts/build.js +125 -125
  129. package/scripts/docker-compose.yml +111 -111
  130. package/scripts/next-release-howto.md +24 -24
  131. package/scripts/release.sh +34 -34
  132. package/scripts/runkit-example.js +34 -34
  133. package/scripts/stress-test/README.txt +18 -18
  134. package/scripts/stress-test/docker-compose.yml +47 -47
  135. package/scripts/stress-test/knex-stress-test.js +196 -196
  136. package/scripts/stress-test/mysql2-random-hanging-every-now-and-then.js +145 -145
  137. package/scripts/stress-test/mysql2-sudden-exit-without-error.js +100 -100
  138. package/scripts/stress-test/reconnect-test-mysql-based-drivers.js +184 -184
  139. package/types/index.d.ts +2249 -2249
  140. package/types/result.d.ts +27 -27
  141. package/types/tables.d.ts +4 -4
@@ -1,889 +1,889 @@
1
- // Query Compiler
2
- // -------
3
- const helpers = require('../helpers');
4
- const Raw = require('../raw');
5
- const QueryBuilder = require('./builder');
6
- const JoinClause = require('./joinclause');
7
- const debug = require('debug');
8
-
9
- const assign = require('lodash/assign');
10
- const bind = require('lodash/bind');
11
- const compact = require('lodash/compact');
12
- const groupBy = require('lodash/groupBy');
13
- const has = require('lodash/has');
14
- const isEmpty = require('lodash/isEmpty');
15
- const map = require('lodash/map');
16
- const omitBy = require('lodash/omitBy');
17
- const reduce = require('lodash/reduce');
18
- const { nanoid } = require('../util/nanoid');
19
- const { isString, isUndefined } = require('../util/is');
20
-
21
- const debugBindings = debug('knex:bindings');
22
-
23
- const components = [
24
- 'columns',
25
- 'join',
26
- 'where',
27
- 'union',
28
- 'group',
29
- 'having',
30
- 'order',
31
- 'limit',
32
- 'offset',
33
- 'lock',
34
- 'waitMode',
35
- ];
36
-
37
- // The "QueryCompiler" takes all of the query statements which
38
- // have been gathered in the "QueryBuilder" and turns them into a
39
- // properly formatted / bound query string.
40
- class QueryCompiler {
41
- constructor(client, builder) {
42
- this.client = client;
43
- this.method = builder._method || 'select';
44
- this.options = builder._options;
45
- this.single = builder._single;
46
- this.timeout = builder._timeout || false;
47
- this.cancelOnTimeout = builder._cancelOnTimeout || false;
48
- this.grouped = groupBy(builder._statements, 'grouping');
49
- this.formatter = client.formatter(builder);
50
- // Used when the insert call is empty.
51
- this._emptyInsertValue = 'default values';
52
- this.first = this.select;
53
- }
54
-
55
- // Collapse the builder into a single object
56
- toSQL(method, tz) {
57
- this._undefinedInWhereClause = false;
58
- this.undefinedBindingsInfo = [];
59
-
60
- method = method || this.method;
61
- const val = this[method]() || '';
62
-
63
- const query = {
64
- method,
65
- options: reduce(this.options, assign, {}),
66
- timeout: this.timeout,
67
- cancelOnTimeout: this.cancelOnTimeout,
68
- bindings: this.formatter.bindings || [],
69
- __knexQueryUid: nanoid(),
70
- };
71
-
72
- Object.defineProperties(query, {
73
- toNative: {
74
- value: () => {
75
- return {
76
- sql: this.client.positionBindings(query.sql),
77
- bindings: this.client.prepBindings(query.bindings),
78
- };
79
- },
80
- enumerable: false,
81
- },
82
- });
83
-
84
- if (isString(val)) {
85
- query.sql = val;
86
- } else {
87
- assign(query, val);
88
- }
89
-
90
- if (method === 'select' || method === 'first') {
91
- if (this.single.as) {
92
- query.as = this.single.as;
93
- }
94
- }
95
-
96
- if (this._undefinedInWhereClause) {
97
- debugBindings(query.bindings);
98
- throw new Error(
99
- `Undefined binding(s) detected when compiling ` +
100
- `${method.toUpperCase()}. Undefined column(s): [${this.undefinedBindingsInfo.join(
101
- ', '
102
- )}] query: ${query.sql}`
103
- );
104
- }
105
-
106
- return query;
107
- }
108
-
109
- // Compiles the `select` statement, or nested sub-selects by calling each of
110
- // the component compilers, trimming out the empties, and returning a
111
- // generated query string.
112
- select() {
113
- let sql = this.with();
114
-
115
- const statements = components.map((component) => this[component](this));
116
- sql += compact(statements).join(' ');
117
- return sql;
118
- }
119
-
120
- pluck() {
121
- let toPluck = this.single.pluck;
122
- if (toPluck.indexOf('.') !== -1) {
123
- toPluck = toPluck.split('.').slice(-1)[0];
124
- }
125
- return {
126
- sql: this.select(),
127
- pluck: toPluck,
128
- };
129
- }
130
-
131
- // Compiles an "insert" query, allowing for multiple
132
- // inserts using a single query statement.
133
- insert() {
134
- const insertValues = this.single.insert || [];
135
- let sql = this.with() + `insert into ${this.tableName} `;
136
- if (Array.isArray(insertValues)) {
137
- if (insertValues.length === 0) {
138
- return '';
139
- }
140
- } else if (typeof insertValues === 'object' && isEmpty(insertValues)) {
141
- return sql + this._emptyInsertValue;
142
- }
143
-
144
- const insertData = this._prepInsert(insertValues);
145
- if (typeof insertData === 'string') {
146
- sql += insertData;
147
- } else {
148
- if (insertData.columns.length) {
149
- sql += `(${this.formatter.columnize(insertData.columns)}`;
150
- sql += ') values (';
151
- let i = -1;
152
- while (++i < insertData.values.length) {
153
- if (i !== 0) sql += '), (';
154
- sql += this.formatter.parameterize(
155
- insertData.values[i],
156
- this.client.valueForUndefined
157
- );
158
- }
159
- sql += ')';
160
- } else if (insertValues.length === 1 && insertValues[0]) {
161
- sql += this._emptyInsertValue;
162
- } else {
163
- sql = '';
164
- }
165
- }
166
- return sql;
167
- }
168
-
169
- // Compiles the "update" query.
170
- update() {
171
- // Make sure tableName is processed by the formatter first.
172
- const withSQL = this.with();
173
- const { tableName } = this;
174
- const updateData = this._prepUpdate(this.single.update);
175
- const wheres = this.where();
176
- return (
177
- withSQL +
178
- `update ${this.single.only ? 'only ' : ''}${tableName}` +
179
- ' set ' +
180
- updateData.join(', ') +
181
- (wheres ? ` ${wheres}` : '')
182
- );
183
- }
184
-
185
- _hintComments() {
186
- let hints = this.grouped.hintComments || [];
187
- hints = hints.map((hint) => compact(hint.value).join(' '));
188
- hints = compact(hints).join(' ');
189
- return hints ? `/*+ ${hints} */ ` : ''
190
- }
191
-
192
- // Compiles the columns in the query, specifying if an item was distinct.
193
- columns() {
194
- let distinctClause = '';
195
- if (this.onlyUnions()) return '';
196
- const hints = this._hintComments()
197
- const columns = this.grouped.columns || [];
198
- let i = -1,
199
- sql = [];
200
- if (columns) {
201
- while (++i < columns.length) {
202
- const stmt = columns[i];
203
- if (stmt.distinct) distinctClause = 'distinct ';
204
- if (stmt.distinctOn) {
205
- distinctClause = this.distinctOn(stmt.value);
206
- continue;
207
- }
208
- if (stmt.type === 'aggregate') {
209
- sql.push(...this.aggregate(stmt));
210
- } else if (stmt.type === 'aggregateRaw') {
211
- sql.push(this.aggregateRaw(stmt));
212
- } else if (stmt.value && stmt.value.length > 0) {
213
- sql.push(this.formatter.columnize(stmt.value));
214
- }
215
- }
216
- }
217
- if (sql.length === 0) sql = ['*'];
218
- return (
219
- `select ${hints}${distinctClause}` +
220
- sql.join(', ') +
221
- (this.tableName
222
- ? ` from ${this.single.only ? 'only ' : ''}${this.tableName}`
223
- : '')
224
- );
225
- }
226
-
227
- _aggregate(stmt, { aliasSeparator = ' as ', distinctParentheses } = {}) {
228
- const value = stmt.value;
229
- const method = stmt.method;
230
- const distinct = stmt.aggregateDistinct ? 'distinct ' : '';
231
- const wrap = (identifier) => this.formatter.wrap(identifier);
232
- const addAlias = (value, alias) => {
233
- if (alias) {
234
- return value + aliasSeparator + wrap(alias);
235
- }
236
- return value;
237
- };
238
- const aggregateArray = (value, alias) => {
239
- let columns = value.map(wrap).join(', ');
240
- if (distinct) {
241
- const openParen = distinctParentheses ? '(' : ' ';
242
- const closeParen = distinctParentheses ? ')' : '';
243
- columns = distinct.trim() + openParen + columns + closeParen;
244
- }
245
- const aggregated = `${method}(${columns})`;
246
- return addAlias(aggregated, alias);
247
- };
248
- const aggregateString = (value, alias) => {
249
- const aggregated = `${method}(${distinct + wrap(value)})`;
250
- return addAlias(aggregated, alias);
251
- };
252
-
253
- if (Array.isArray(value)) {
254
- return [aggregateArray(value)];
255
- }
256
-
257
- if (typeof value === 'object') {
258
- if (stmt.alias) {
259
- throw new Error('When using an object explicit alias can not be used');
260
- }
261
- return Object.entries(value).map(([alias, column]) => {
262
- if (Array.isArray(column)) {
263
- return aggregateArray(column, alias);
264
- }
265
- return aggregateString(column, alias);
266
- });
267
- }
268
-
269
- // Allows us to speciy an alias for the aggregate types.
270
- const splitOn = value.toLowerCase().indexOf(' as ');
271
- let column = value;
272
- let { alias } = stmt;
273
- if (splitOn !== -1) {
274
- column = value.slice(0, splitOn);
275
- if (alias) {
276
- throw new Error(`Found multiple aliases for same column: ${column}`);
277
- }
278
- alias = value.slice(splitOn + 4);
279
- }
280
- return [aggregateString(column, alias)];
281
- }
282
-
283
- aggregate(stmt) {
284
- return this._aggregate(stmt);
285
- }
286
-
287
- aggregateRaw(stmt) {
288
- const distinct = stmt.aggregateDistinct ? 'distinct ' : '';
289
- return `${stmt.method}(${distinct + this.formatter.unwrapRaw(stmt.value)})`;
290
- }
291
-
292
- // Compiles all each of the `join` clauses on the query,
293
- // including any nested join queries.
294
- join() {
295
- let sql = '';
296
- let i = -1;
297
- const joins = this.grouped.join;
298
- if (!joins) return '';
299
- while (++i < joins.length) {
300
- const join = joins[i];
301
- const table = join.schema ? `${join.schema}.${join.table}` : join.table;
302
- if (i > 0) sql += ' ';
303
- if (join.joinType === 'raw') {
304
- sql += this.formatter.unwrapRaw(join.table);
305
- } else {
306
- sql += join.joinType + ' join ' + this.formatter.wrap(table);
307
- let ii = -1;
308
- while (++ii < join.clauses.length) {
309
- const clause = join.clauses[ii];
310
- if (ii > 0) {
311
- sql += ` ${clause.bool} `;
312
- } else {
313
- sql += ` ${clause.type === 'onUsing' ? 'using' : 'on'} `;
314
- }
315
- const val = this[clause.type].call(this, clause);
316
- if (val) {
317
- sql += val;
318
- }
319
- }
320
- }
321
- }
322
- return sql;
323
- }
324
-
325
- onBetween(statement) {
326
- return (
327
- this.formatter.wrap(statement.column) +
328
- ' ' +
329
- this._not(statement, 'between') +
330
- ' ' +
331
- map(statement.value, bind(this.formatter.parameter, this.formatter)).join(
332
- ' and '
333
- )
334
- );
335
- }
336
-
337
- onNull(statement) {
338
- return (
339
- this.formatter.wrap(statement.column) +
340
- ' is ' +
341
- this._not(statement, 'null')
342
- );
343
- }
344
-
345
- onExists(statement) {
346
- return (
347
- this._not(statement, 'exists') +
348
- ' (' +
349
- this.formatter.rawOrFn(statement.value) +
350
- ')'
351
- );
352
- }
353
-
354
- onIn(statement) {
355
- if (Array.isArray(statement.column)) return this.multiOnIn(statement);
356
- return (
357
- this.formatter.wrap(statement.column) +
358
- ' ' +
359
- this._not(statement, 'in ') +
360
- this.wrap(this.formatter.parameterize(statement.value))
361
- );
362
- }
363
-
364
- multiOnIn(statement) {
365
- let i = -1,
366
- sql = `(${this.formatter.columnize(statement.column)}) `;
367
- sql += this._not(statement, 'in ') + '((';
368
- while (++i < statement.value.length) {
369
- if (i !== 0) sql += '),(';
370
- sql += this.formatter.parameterize(statement.value[i]);
371
- }
372
- return sql + '))';
373
- }
374
-
375
- // Compiles all `where` statements on the query.
376
- where() {
377
- const wheres = this.grouped.where;
378
- if (!wheres) return;
379
- const sql = [];
380
- let i = -1;
381
- while (++i < wheres.length) {
382
- const stmt = wheres[i];
383
- if (
384
- Object.prototype.hasOwnProperty.call(stmt, 'value') &&
385
- helpers.containsUndefined(stmt.value)
386
- ) {
387
- this.undefinedBindingsInfo.push(stmt.column);
388
- this._undefinedInWhereClause = true;
389
- }
390
- const val = this[stmt.type](stmt);
391
- if (val) {
392
- if (sql.length === 0) {
393
- sql[0] = 'where';
394
- } else {
395
- sql.push(stmt.bool);
396
- }
397
- sql.push(val);
398
- }
399
- }
400
- return sql.length > 1 ? sql.join(' ') : '';
401
- }
402
-
403
- group() {
404
- return this._groupsOrders('group');
405
- }
406
-
407
- order() {
408
- return this._groupsOrders('order');
409
- }
410
-
411
- // Compiles the `having` statements.
412
- having() {
413
- const havings = this.grouped.having;
414
- if (!havings) return '';
415
- const sql = ['having'];
416
- for (let i = 0, l = havings.length; i < l; i++) {
417
- const s = havings[i];
418
- const val = this[s.type](s);
419
- if (val) {
420
- if (sql.length === 0) {
421
- sql[0] = 'where';
422
- }
423
- if (sql.length > 1 || (sql.length === 1 && sql[0] !== 'having')) {
424
- sql.push(s.bool);
425
- }
426
- sql.push(val);
427
- }
428
- }
429
- return sql.length > 1 ? sql.join(' ') : '';
430
- }
431
-
432
- havingRaw(statement) {
433
- return this._not(statement, '') + this.formatter.unwrapRaw(statement.value);
434
- }
435
-
436
- havingWrapped(statement) {
437
- const val = this.formatter.rawOrFn(statement.value, 'where');
438
- return (val && this._not(statement, '') + '(' + val.slice(6) + ')') || '';
439
- }
440
-
441
- havingBasic(statement) {
442
- return (
443
- this._not(statement, '') +
444
- this.formatter.wrap(statement.column) +
445
- ' ' +
446
- this.formatter.operator(statement.operator) +
447
- ' ' +
448
- this.formatter.parameter(statement.value)
449
- );
450
- }
451
-
452
- havingNull(statement) {
453
- return (
454
- this.formatter.wrap(statement.column) +
455
- ' is ' +
456
- this._not(statement, 'null')
457
- );
458
- }
459
-
460
- havingExists(statement) {
461
- return (
462
- this._not(statement, 'exists') +
463
- ' (' +
464
- this.formatter.rawOrFn(statement.value) +
465
- ')'
466
- );
467
- }
468
-
469
- havingBetween(statement) {
470
- return (
471
- this.formatter.wrap(statement.column) +
472
- ' ' +
473
- this._not(statement, 'between') +
474
- ' ' +
475
- map(statement.value, bind(this.formatter.parameter, this.formatter)).join(
476
- ' and '
477
- )
478
- );
479
- }
480
-
481
- havingIn(statement) {
482
- if (Array.isArray(statement.column)) return this.multiHavingIn(statement);
483
- return (
484
- this.formatter.wrap(statement.column) +
485
- ' ' +
486
- this._not(statement, 'in ') +
487
- this.wrap(this.formatter.parameterize(statement.value))
488
- );
489
- }
490
-
491
- multiHavingIn(statement) {
492
- let i = -1,
493
- sql = `(${this.formatter.columnize(statement.column)}) `;
494
- sql += this._not(statement, 'in ') + '((';
495
- while (++i < statement.value.length) {
496
- if (i !== 0) sql += '),(';
497
- sql += this.formatter.parameterize(statement.value[i]);
498
- }
499
- return sql + '))';
500
- }
501
-
502
- // Compile the "union" queries attached to the main query.
503
- union() {
504
- const onlyUnions = this.onlyUnions();
505
- const unions = this.grouped.union;
506
- if (!unions) return '';
507
- let sql = '';
508
- for (let i = 0, l = unions.length; i < l; i++) {
509
- const union = unions[i];
510
- if (i > 0) sql += ' ';
511
- if (i > 0 || !onlyUnions) sql += union.clause + ' ';
512
- const statement = this.formatter.rawOrFn(union.value);
513
- if (statement) {
514
- if (union.wrap) sql += '(';
515
- sql += statement;
516
- if (union.wrap) sql += ')';
517
- }
518
- }
519
- return sql;
520
- }
521
-
522
- // If we haven't specified any columns or a `tableName`, we're assuming this
523
- // is only being used for unions.
524
- onlyUnions() {
525
- return !this.grouped.columns && this.grouped.union && !this.tableName;
526
- }
527
-
528
- limit() {
529
- const noLimit = !this.single.limit && this.single.limit !== 0;
530
- if (noLimit) return '';
531
- return `limit ${this.formatter.parameter(this.single.limit)}`;
532
- }
533
-
534
- offset() {
535
- if (!this.single.offset) return '';
536
- return `offset ${this.formatter.parameter(this.single.offset)}`;
537
- }
538
-
539
- // Compiles a `delete` query.
540
- del() {
541
- // Make sure tableName is processed by the formatter first.
542
- const { tableName } = this;
543
- const withSQL = this.with();
544
- const wheres = this.where();
545
- return (
546
- withSQL +
547
- `delete from ${this.single.only ? 'only ' : ''}${tableName}` +
548
- (wheres ? ` ${wheres}` : '')
549
- );
550
- }
551
-
552
- // Compiles a `truncate` query.
553
- truncate() {
554
- return `truncate ${this.tableName}`;
555
- }
556
-
557
- // Compiles the "locks".
558
- lock() {
559
- if (this.single.lock) {
560
- return this[this.single.lock]();
561
- }
562
- }
563
-
564
- // Compiles the wait mode on the locks.
565
- waitMode() {
566
- if (this.single.waitMode) {
567
- return this[this.single.waitMode]();
568
- }
569
- }
570
-
571
- // Fail on unsupported databases
572
- skipLocked() {
573
- throw new Error(
574
- '.skipLocked() is currently only supported on MySQL 8.0+ and PostgreSQL 9.5+'
575
- );
576
- }
577
-
578
- // Fail on unsupported databases
579
- noWait() {
580
- throw new Error(
581
- '.noWait() is currently only supported on MySQL 8.0+, MariaDB 10.3.0+ and PostgreSQL 9.5+'
582
- );
583
- }
584
-
585
- distinctOn(value) {
586
- throw new Error('.distinctOn() is currently only supported on PostgreSQL');
587
- }
588
-
589
- // On Clause
590
- // ------
591
-
592
- onWrapped(clause) {
593
- const self = this;
594
-
595
- const wrapJoin = new JoinClause();
596
- clause.value.call(wrapJoin, wrapJoin);
597
-
598
- let sql = '';
599
- wrapJoin.clauses.forEach(function (wrapClause, ii) {
600
- if (ii > 0) {
601
- sql += ` ${wrapClause.bool} `;
602
- }
603
- const val = self[wrapClause.type](wrapClause);
604
- if (val) {
605
- sql += val;
606
- }
607
- });
608
-
609
- if (sql.length) {
610
- return `(${sql})`;
611
- }
612
- return '';
613
- }
614
-
615
- onBasic(clause) {
616
- return (
617
- this.formatter.wrap(clause.column) +
618
- ' ' +
619
- this.formatter.operator(clause.operator) +
620
- ' ' +
621
- this.formatter.wrap(clause.value)
622
- );
623
- }
624
-
625
- onVal(clause) {
626
- return (
627
- this.formatter.wrap(clause.column) +
628
- ' ' +
629
- this.formatter.operator(clause.operator) +
630
- ' ' +
631
- this.formatter.parameter(clause.value)
632
- );
633
- }
634
-
635
- onRaw(clause) {
636
- return this.formatter.unwrapRaw(clause.value);
637
- }
638
-
639
- onUsing(clause) {
640
- return '(' + this.formatter.columnize(clause.column) + ')';
641
- }
642
-
643
- // Where Clause
644
- // ------
645
-
646
- whereIn(statement) {
647
- let columns = null;
648
- if (Array.isArray(statement.column)) {
649
- columns = `(${this.formatter.columnize(statement.column)})`;
650
- } else {
651
- columns = this.formatter.wrap(statement.column);
652
- }
653
-
654
- const values = this.formatter.values(statement.value);
655
- return `${columns} ${this._not(statement, 'in ')}${values}`;
656
- }
657
-
658
- whereNull(statement) {
659
- return (
660
- this.formatter.wrap(statement.column) +
661
- ' is ' +
662
- this._not(statement, 'null')
663
- );
664
- }
665
-
666
- // Compiles a basic "where" clause.
667
- whereBasic(statement) {
668
- return (
669
- this._not(statement, '') +
670
- this.formatter.wrap(statement.column) +
671
- ' ' +
672
- this.formatter.operator(statement.operator) +
673
- ' ' +
674
- (statement.asColumn
675
- ? this.formatter.wrap(statement.value)
676
- : this.formatter.parameter(statement.value))
677
- );
678
- }
679
-
680
- whereExists(statement) {
681
- return (
682
- this._not(statement, 'exists') +
683
- ' (' +
684
- this.formatter.rawOrFn(statement.value) +
685
- ')'
686
- );
687
- }
688
-
689
- whereWrapped(statement) {
690
- const val = this.formatter.rawOrFn(statement.value, 'where');
691
- return (val && this._not(statement, '') + '(' + val.slice(6) + ')') || '';
692
- }
693
-
694
- whereBetween(statement) {
695
- return (
696
- this.formatter.wrap(statement.column) +
697
- ' ' +
698
- this._not(statement, 'between') +
699
- ' ' +
700
- map(statement.value, bind(this.formatter.parameter, this.formatter)).join(
701
- ' and '
702
- )
703
- );
704
- }
705
-
706
- // Compiles a "whereRaw" query.
707
- whereRaw(statement) {
708
- return this._not(statement, '') + this.formatter.unwrapRaw(statement.value);
709
- }
710
-
711
- wrap(str) {
712
- if (str.charAt(0) !== '(') return `(${str})`;
713
- return str;
714
- }
715
-
716
- // Compiles all `with` statements on the query.
717
- with() {
718
- if (!this.grouped.with || !this.grouped.with.length) {
719
- return '';
720
- }
721
- const withs = this.grouped.with;
722
- if (!withs) return;
723
- const sql = [];
724
- let i = -1;
725
- let isRecursive = false;
726
- while (++i < withs.length) {
727
- const stmt = withs[i];
728
- if (stmt.recursive) {
729
- isRecursive = true;
730
- }
731
- const val = this[stmt.type](stmt);
732
- sql.push(val);
733
- }
734
- return `with ${isRecursive ? 'recursive ' : ''}${sql.join(', ')} `;
735
- }
736
-
737
- withWrapped(statement) {
738
- const val = this.formatter.rawOrFn(statement.value);
739
- return (
740
- (val &&
741
- this.formatter.columnize(statement.alias) + ' as (' + val + ')') ||
742
- ''
743
- );
744
- }
745
-
746
- // Determines whether to add a "not" prefix to the where clause.
747
- _not(statement, str) {
748
- if (statement.not) return `not ${str}`;
749
- return str;
750
- }
751
-
752
- _prepInsert(data) {
753
- const isRaw = this.formatter.rawOrFn(data);
754
- if (isRaw) return isRaw;
755
- let columns = [];
756
- const values = [];
757
- if (!Array.isArray(data)) data = data ? [data] : [];
758
- let i = -1;
759
- while (++i < data.length) {
760
- if (data[i] == null) break;
761
- if (i === 0) columns = Object.keys(data[i]).sort();
762
- const row = new Array(columns.length);
763
- const keys = Object.keys(data[i]);
764
- let j = -1;
765
- while (++j < keys.length) {
766
- const key = keys[j];
767
- let idx = columns.indexOf(key);
768
- if (idx === -1) {
769
- columns = columns.concat(key).sort();
770
- idx = columns.indexOf(key);
771
- let k = -1;
772
- while (++k < values.length) {
773
- values[k].splice(idx, 0, undefined);
774
- }
775
- row.splice(idx, 0, undefined);
776
- }
777
- row[idx] = data[i][key];
778
- }
779
- values.push(row);
780
- }
781
- return {
782
- columns,
783
- values,
784
- };
785
- }
786
-
787
- // "Preps" the update.
788
- _prepUpdate(data = {}) {
789
- const { counter = {} } = this.single;
790
-
791
- for (const column of Object.keys(counter)) {
792
- //Skip?
793
- if (has(data, column)) {
794
- //Needed?
795
- this.client.logger.warn(
796
- `increment/decrement called for a column that has already been specified in main .update() call. Ignoring increment/decrement and using value from .update() call.`
797
- );
798
- continue;
799
- }
800
-
801
- let value = counter[column];
802
-
803
- const symbol = value < 0 ? '-' : '+';
804
-
805
- if (symbol === '-') {
806
- value = -value;
807
- }
808
-
809
- data[column] = this.client.raw(`?? ${symbol} ?`, [column, value]);
810
- }
811
-
812
- data = omitBy(data, isUndefined);
813
-
814
- const vals = [];
815
- const columns = Object.keys(data);
816
- let i = -1;
817
-
818
- while (++i < columns.length) {
819
- vals.push(
820
- this.formatter.wrap(columns[i]) +
821
- ' = ' +
822
- this.formatter.parameter(data[columns[i]])
823
- );
824
- }
825
-
826
- if (isEmpty(vals)) {
827
- throw new Error(
828
- [
829
- 'Empty .update() call detected!',
830
- 'Update data does not contain any values to update.',
831
- 'This will result in a faulty query.',
832
- this.single.table ? `Table: ${this.single.table}.` : '',
833
- this.single.update
834
- ? `Columns: ${Object.keys(this.single.update)}.`
835
- : '',
836
- ].join(' ')
837
- );
838
- }
839
-
840
- return vals;
841
- }
842
-
843
- _formatGroupsItemValue(value) {
844
- const { formatter } = this;
845
- if (value instanceof Raw) {
846
- return formatter.unwrapRaw(value);
847
- } else if (value instanceof QueryBuilder) {
848
- return '(' + formatter.columnize(value) + ')';
849
- } else {
850
- return formatter.columnize(value);
851
- }
852
- }
853
-
854
- // Compiles the `order by` statements.
855
- _groupsOrders(type) {
856
- const items = this.grouped[type];
857
- if (!items) return '';
858
- const { formatter } = this;
859
- const sql = items.map((item) => {
860
- const column = this._formatGroupsItemValue(item.value);
861
- const direction =
862
- type === 'order' && item.type !== 'orderByRaw'
863
- ? ` ${formatter.direction(item.direction)}`
864
- : '';
865
- return column + direction;
866
- });
867
- return sql.length ? type + ' by ' + sql.join(', ') : '';
868
- }
869
-
870
- // Get the table name, wrapping it if necessary.
871
- // Implemented as a property to prevent ordering issues as described in #704.
872
- get tableName() {
873
- if (!this._tableName) {
874
- // Only call this.formatter.wrap() the first time this property is accessed.
875
- let tableName = this.single.table;
876
- const schemaName = this.single.schema;
877
-
878
- if (tableName && schemaName) tableName = `${schemaName}.${tableName}`;
879
-
880
- this._tableName = tableName
881
- ? // Wrap subQuery with parenthesis, #3485
882
- this.formatter.wrap(tableName, tableName instanceof QueryBuilder)
883
- : '';
884
- }
885
- return this._tableName;
886
- }
887
- }
888
-
889
- module.exports = QueryCompiler;
1
+ // Query Compiler
2
+ // -------
3
+ const helpers = require('../helpers');
4
+ const Raw = require('../raw');
5
+ const QueryBuilder = require('./builder');
6
+ const JoinClause = require('./joinclause');
7
+ const debug = require('debug');
8
+
9
+ const assign = require('lodash/assign');
10
+ const bind = require('lodash/bind');
11
+ const compact = require('lodash/compact');
12
+ const groupBy = require('lodash/groupBy');
13
+ const has = require('lodash/has');
14
+ const isEmpty = require('lodash/isEmpty');
15
+ const map = require('lodash/map');
16
+ const omitBy = require('lodash/omitBy');
17
+ const reduce = require('lodash/reduce');
18
+ const { nanoid } = require('../util/nanoid');
19
+ const { isString, isUndefined } = require('../util/is');
20
+
21
+ const debugBindings = debug('knex:bindings');
22
+
23
+ const components = [
24
+ 'columns',
25
+ 'join',
26
+ 'where',
27
+ 'union',
28
+ 'group',
29
+ 'having',
30
+ 'order',
31
+ 'limit',
32
+ 'offset',
33
+ 'lock',
34
+ 'waitMode',
35
+ ];
36
+
37
+ // The "QueryCompiler" takes all of the query statements which
38
+ // have been gathered in the "QueryBuilder" and turns them into a
39
+ // properly formatted / bound query string.
40
+ class QueryCompiler {
41
+ constructor(client, builder) {
42
+ this.client = client;
43
+ this.method = builder._method || 'select';
44
+ this.options = builder._options;
45
+ this.single = builder._single;
46
+ this.timeout = builder._timeout || false;
47
+ this.cancelOnTimeout = builder._cancelOnTimeout || false;
48
+ this.grouped = groupBy(builder._statements, 'grouping');
49
+ this.formatter = client.formatter(builder);
50
+ // Used when the insert call is empty.
51
+ this._emptyInsertValue = 'default values';
52
+ this.first = this.select;
53
+ }
54
+
55
+ // Collapse the builder into a single object
56
+ toSQL(method, tz) {
57
+ this._undefinedInWhereClause = false;
58
+ this.undefinedBindingsInfo = [];
59
+
60
+ method = method || this.method;
61
+ const val = this[method]() || '';
62
+
63
+ const query = {
64
+ method,
65
+ options: reduce(this.options, assign, {}),
66
+ timeout: this.timeout,
67
+ cancelOnTimeout: this.cancelOnTimeout,
68
+ bindings: this.formatter.bindings || [],
69
+ __knexQueryUid: nanoid(),
70
+ };
71
+
72
+ Object.defineProperties(query, {
73
+ toNative: {
74
+ value: () => {
75
+ return {
76
+ sql: this.client.positionBindings(query.sql),
77
+ bindings: this.client.prepBindings(query.bindings),
78
+ };
79
+ },
80
+ enumerable: false,
81
+ },
82
+ });
83
+
84
+ if (isString(val)) {
85
+ query.sql = val;
86
+ } else {
87
+ assign(query, val);
88
+ }
89
+
90
+ if (method === 'select' || method === 'first') {
91
+ if (this.single.as) {
92
+ query.as = this.single.as;
93
+ }
94
+ }
95
+
96
+ if (this._undefinedInWhereClause) {
97
+ debugBindings(query.bindings);
98
+ throw new Error(
99
+ `Undefined binding(s) detected when compiling ` +
100
+ `${method.toUpperCase()}. Undefined column(s): [${this.undefinedBindingsInfo.join(
101
+ ', '
102
+ )}] query: ${query.sql}`
103
+ );
104
+ }
105
+
106
+ return query;
107
+ }
108
+
109
+ // Compiles the `select` statement, or nested sub-selects by calling each of
110
+ // the component compilers, trimming out the empties, and returning a
111
+ // generated query string.
112
+ select() {
113
+ let sql = this.with();
114
+
115
+ const statements = components.map((component) => this[component](this));
116
+ sql += compact(statements).join(' ');
117
+ return sql;
118
+ }
119
+
120
+ pluck() {
121
+ let toPluck = this.single.pluck;
122
+ if (toPluck.indexOf('.') !== -1) {
123
+ toPluck = toPluck.split('.').slice(-1)[0];
124
+ }
125
+ return {
126
+ sql: this.select(),
127
+ pluck: toPluck,
128
+ };
129
+ }
130
+
131
+ // Compiles an "insert" query, allowing for multiple
132
+ // inserts using a single query statement.
133
+ insert() {
134
+ const insertValues = this.single.insert || [];
135
+ let sql = this.with() + `insert into ${this.tableName} `;
136
+ if (Array.isArray(insertValues)) {
137
+ if (insertValues.length === 0) {
138
+ return '';
139
+ }
140
+ } else if (typeof insertValues === 'object' && isEmpty(insertValues)) {
141
+ return sql + this._emptyInsertValue;
142
+ }
143
+
144
+ const insertData = this._prepInsert(insertValues);
145
+ if (typeof insertData === 'string') {
146
+ sql += insertData;
147
+ } else {
148
+ if (insertData.columns.length) {
149
+ sql += `(${this.formatter.columnize(insertData.columns)}`;
150
+ sql += ') values (';
151
+ let i = -1;
152
+ while (++i < insertData.values.length) {
153
+ if (i !== 0) sql += '), (';
154
+ sql += this.formatter.parameterize(
155
+ insertData.values[i],
156
+ this.client.valueForUndefined
157
+ );
158
+ }
159
+ sql += ')';
160
+ } else if (insertValues.length === 1 && insertValues[0]) {
161
+ sql += this._emptyInsertValue;
162
+ } else {
163
+ sql = '';
164
+ }
165
+ }
166
+ return sql;
167
+ }
168
+
169
+ // Compiles the "update" query.
170
+ update() {
171
+ // Make sure tableName is processed by the formatter first.
172
+ const withSQL = this.with();
173
+ const { tableName } = this;
174
+ const updateData = this._prepUpdate(this.single.update);
175
+ const wheres = this.where();
176
+ return (
177
+ withSQL +
178
+ `update ${this.single.only ? 'only ' : ''}${tableName}` +
179
+ ' set ' +
180
+ updateData.join(', ') +
181
+ (wheres ? ` ${wheres}` : '')
182
+ );
183
+ }
184
+
185
+ _hintComments() {
186
+ let hints = this.grouped.hintComments || [];
187
+ hints = hints.map((hint) => compact(hint.value).join(' '));
188
+ hints = compact(hints).join(' ');
189
+ return hints ? `/*+ ${hints} */ ` : ''
190
+ }
191
+
192
+ // Compiles the columns in the query, specifying if an item was distinct.
193
+ columns() {
194
+ let distinctClause = '';
195
+ if (this.onlyUnions()) return '';
196
+ const hints = this._hintComments()
197
+ const columns = this.grouped.columns || [];
198
+ let i = -1,
199
+ sql = [];
200
+ if (columns) {
201
+ while (++i < columns.length) {
202
+ const stmt = columns[i];
203
+ if (stmt.distinct) distinctClause = 'distinct ';
204
+ if (stmt.distinctOn) {
205
+ distinctClause = this.distinctOn(stmt.value);
206
+ continue;
207
+ }
208
+ if (stmt.type === 'aggregate') {
209
+ sql.push(...this.aggregate(stmt));
210
+ } else if (stmt.type === 'aggregateRaw') {
211
+ sql.push(this.aggregateRaw(stmt));
212
+ } else if (stmt.value && stmt.value.length > 0) {
213
+ sql.push(this.formatter.columnize(stmt.value));
214
+ }
215
+ }
216
+ }
217
+ if (sql.length === 0) sql = ['*'];
218
+ return (
219
+ `select ${hints}${distinctClause}` +
220
+ sql.join(', ') +
221
+ (this.tableName
222
+ ? ` from ${this.single.only ? 'only ' : ''}${this.tableName}`
223
+ : '')
224
+ );
225
+ }
226
+
227
+ _aggregate(stmt, { aliasSeparator = ' as ', distinctParentheses } = {}) {
228
+ const value = stmt.value;
229
+ const method = stmt.method;
230
+ const distinct = stmt.aggregateDistinct ? 'distinct ' : '';
231
+ const wrap = (identifier) => this.formatter.wrap(identifier);
232
+ const addAlias = (value, alias) => {
233
+ if (alias) {
234
+ return value + aliasSeparator + wrap(alias);
235
+ }
236
+ return value;
237
+ };
238
+ const aggregateArray = (value, alias) => {
239
+ let columns = value.map(wrap).join(', ');
240
+ if (distinct) {
241
+ const openParen = distinctParentheses ? '(' : ' ';
242
+ const closeParen = distinctParentheses ? ')' : '';
243
+ columns = distinct.trim() + openParen + columns + closeParen;
244
+ }
245
+ const aggregated = `${method}(${columns})`;
246
+ return addAlias(aggregated, alias);
247
+ };
248
+ const aggregateString = (value, alias) => {
249
+ const aggregated = `${method}(${distinct + wrap(value)})`;
250
+ return addAlias(aggregated, alias);
251
+ };
252
+
253
+ if (Array.isArray(value)) {
254
+ return [aggregateArray(value)];
255
+ }
256
+
257
+ if (typeof value === 'object') {
258
+ if (stmt.alias) {
259
+ throw new Error('When using an object explicit alias can not be used');
260
+ }
261
+ return Object.entries(value).map(([alias, column]) => {
262
+ if (Array.isArray(column)) {
263
+ return aggregateArray(column, alias);
264
+ }
265
+ return aggregateString(column, alias);
266
+ });
267
+ }
268
+
269
+ // Allows us to speciy an alias for the aggregate types.
270
+ const splitOn = value.toLowerCase().indexOf(' as ');
271
+ let column = value;
272
+ let { alias } = stmt;
273
+ if (splitOn !== -1) {
274
+ column = value.slice(0, splitOn);
275
+ if (alias) {
276
+ throw new Error(`Found multiple aliases for same column: ${column}`);
277
+ }
278
+ alias = value.slice(splitOn + 4);
279
+ }
280
+ return [aggregateString(column, alias)];
281
+ }
282
+
283
+ aggregate(stmt) {
284
+ return this._aggregate(stmt);
285
+ }
286
+
287
+ aggregateRaw(stmt) {
288
+ const distinct = stmt.aggregateDistinct ? 'distinct ' : '';
289
+ return `${stmt.method}(${distinct + this.formatter.unwrapRaw(stmt.value)})`;
290
+ }
291
+
292
+ // Compiles all each of the `join` clauses on the query,
293
+ // including any nested join queries.
294
+ join() {
295
+ let sql = '';
296
+ let i = -1;
297
+ const joins = this.grouped.join;
298
+ if (!joins) return '';
299
+ while (++i < joins.length) {
300
+ const join = joins[i];
301
+ const table = join.schema ? `${join.schema}.${join.table}` : join.table;
302
+ if (i > 0) sql += ' ';
303
+ if (join.joinType === 'raw') {
304
+ sql += this.formatter.unwrapRaw(join.table);
305
+ } else {
306
+ sql += join.joinType + ' join ' + this.formatter.wrap(table);
307
+ let ii = -1;
308
+ while (++ii < join.clauses.length) {
309
+ const clause = join.clauses[ii];
310
+ if (ii > 0) {
311
+ sql += ` ${clause.bool} `;
312
+ } else {
313
+ sql += ` ${clause.type === 'onUsing' ? 'using' : 'on'} `;
314
+ }
315
+ const val = this[clause.type].call(this, clause);
316
+ if (val) {
317
+ sql += val;
318
+ }
319
+ }
320
+ }
321
+ }
322
+ return sql;
323
+ }
324
+
325
+ onBetween(statement) {
326
+ return (
327
+ this.formatter.wrap(statement.column) +
328
+ ' ' +
329
+ this._not(statement, 'between') +
330
+ ' ' +
331
+ map(statement.value, bind(this.formatter.parameter, this.formatter)).join(
332
+ ' and '
333
+ )
334
+ );
335
+ }
336
+
337
+ onNull(statement) {
338
+ return (
339
+ this.formatter.wrap(statement.column) +
340
+ ' is ' +
341
+ this._not(statement, 'null')
342
+ );
343
+ }
344
+
345
+ onExists(statement) {
346
+ return (
347
+ this._not(statement, 'exists') +
348
+ ' (' +
349
+ this.formatter.rawOrFn(statement.value) +
350
+ ')'
351
+ );
352
+ }
353
+
354
+ onIn(statement) {
355
+ if (Array.isArray(statement.column)) return this.multiOnIn(statement);
356
+ return (
357
+ this.formatter.wrap(statement.column) +
358
+ ' ' +
359
+ this._not(statement, 'in ') +
360
+ this.wrap(this.formatter.parameterize(statement.value))
361
+ );
362
+ }
363
+
364
+ multiOnIn(statement) {
365
+ let i = -1,
366
+ sql = `(${this.formatter.columnize(statement.column)}) `;
367
+ sql += this._not(statement, 'in ') + '((';
368
+ while (++i < statement.value.length) {
369
+ if (i !== 0) sql += '),(';
370
+ sql += this.formatter.parameterize(statement.value[i]);
371
+ }
372
+ return sql + '))';
373
+ }
374
+
375
+ // Compiles all `where` statements on the query.
376
+ where() {
377
+ const wheres = this.grouped.where;
378
+ if (!wheres) return;
379
+ const sql = [];
380
+ let i = -1;
381
+ while (++i < wheres.length) {
382
+ const stmt = wheres[i];
383
+ if (
384
+ Object.prototype.hasOwnProperty.call(stmt, 'value') &&
385
+ helpers.containsUndefined(stmt.value)
386
+ ) {
387
+ this.undefinedBindingsInfo.push(stmt.column);
388
+ this._undefinedInWhereClause = true;
389
+ }
390
+ const val = this[stmt.type](stmt);
391
+ if (val) {
392
+ if (sql.length === 0) {
393
+ sql[0] = 'where';
394
+ } else {
395
+ sql.push(stmt.bool);
396
+ }
397
+ sql.push(val);
398
+ }
399
+ }
400
+ return sql.length > 1 ? sql.join(' ') : '';
401
+ }
402
+
403
+ group() {
404
+ return this._groupsOrders('group');
405
+ }
406
+
407
+ order() {
408
+ return this._groupsOrders('order');
409
+ }
410
+
411
+ // Compiles the `having` statements.
412
+ having() {
413
+ const havings = this.grouped.having;
414
+ if (!havings) return '';
415
+ const sql = ['having'];
416
+ for (let i = 0, l = havings.length; i < l; i++) {
417
+ const s = havings[i];
418
+ const val = this[s.type](s);
419
+ if (val) {
420
+ if (sql.length === 0) {
421
+ sql[0] = 'where';
422
+ }
423
+ if (sql.length > 1 || (sql.length === 1 && sql[0] !== 'having')) {
424
+ sql.push(s.bool);
425
+ }
426
+ sql.push(val);
427
+ }
428
+ }
429
+ return sql.length > 1 ? sql.join(' ') : '';
430
+ }
431
+
432
+ havingRaw(statement) {
433
+ return this._not(statement, '') + this.formatter.unwrapRaw(statement.value);
434
+ }
435
+
436
+ havingWrapped(statement) {
437
+ const val = this.formatter.rawOrFn(statement.value, 'where');
438
+ return (val && this._not(statement, '') + '(' + val.slice(6) + ')') || '';
439
+ }
440
+
441
+ havingBasic(statement) {
442
+ return (
443
+ this._not(statement, '') +
444
+ this.formatter.wrap(statement.column) +
445
+ ' ' +
446
+ this.formatter.operator(statement.operator) +
447
+ ' ' +
448
+ this.formatter.parameter(statement.value)
449
+ );
450
+ }
451
+
452
+ havingNull(statement) {
453
+ return (
454
+ this.formatter.wrap(statement.column) +
455
+ ' is ' +
456
+ this._not(statement, 'null')
457
+ );
458
+ }
459
+
460
+ havingExists(statement) {
461
+ return (
462
+ this._not(statement, 'exists') +
463
+ ' (' +
464
+ this.formatter.rawOrFn(statement.value) +
465
+ ')'
466
+ );
467
+ }
468
+
469
+ havingBetween(statement) {
470
+ return (
471
+ this.formatter.wrap(statement.column) +
472
+ ' ' +
473
+ this._not(statement, 'between') +
474
+ ' ' +
475
+ map(statement.value, bind(this.formatter.parameter, this.formatter)).join(
476
+ ' and '
477
+ )
478
+ );
479
+ }
480
+
481
+ havingIn(statement) {
482
+ if (Array.isArray(statement.column)) return this.multiHavingIn(statement);
483
+ return (
484
+ this.formatter.wrap(statement.column) +
485
+ ' ' +
486
+ this._not(statement, 'in ') +
487
+ this.wrap(this.formatter.parameterize(statement.value))
488
+ );
489
+ }
490
+
491
+ multiHavingIn(statement) {
492
+ let i = -1,
493
+ sql = `(${this.formatter.columnize(statement.column)}) `;
494
+ sql += this._not(statement, 'in ') + '((';
495
+ while (++i < statement.value.length) {
496
+ if (i !== 0) sql += '),(';
497
+ sql += this.formatter.parameterize(statement.value[i]);
498
+ }
499
+ return sql + '))';
500
+ }
501
+
502
+ // Compile the "union" queries attached to the main query.
503
+ union() {
504
+ const onlyUnions = this.onlyUnions();
505
+ const unions = this.grouped.union;
506
+ if (!unions) return '';
507
+ let sql = '';
508
+ for (let i = 0, l = unions.length; i < l; i++) {
509
+ const union = unions[i];
510
+ if (i > 0) sql += ' ';
511
+ if (i > 0 || !onlyUnions) sql += union.clause + ' ';
512
+ const statement = this.formatter.rawOrFn(union.value);
513
+ if (statement) {
514
+ if (union.wrap) sql += '(';
515
+ sql += statement;
516
+ if (union.wrap) sql += ')';
517
+ }
518
+ }
519
+ return sql;
520
+ }
521
+
522
+ // If we haven't specified any columns or a `tableName`, we're assuming this
523
+ // is only being used for unions.
524
+ onlyUnions() {
525
+ return !this.grouped.columns && this.grouped.union && !this.tableName;
526
+ }
527
+
528
+ limit() {
529
+ const noLimit = !this.single.limit && this.single.limit !== 0;
530
+ if (noLimit) return '';
531
+ return `limit ${this.formatter.parameter(this.single.limit)}`;
532
+ }
533
+
534
+ offset() {
535
+ if (!this.single.offset) return '';
536
+ return `offset ${this.formatter.parameter(this.single.offset)}`;
537
+ }
538
+
539
+ // Compiles a `delete` query.
540
+ del() {
541
+ // Make sure tableName is processed by the formatter first.
542
+ const { tableName } = this;
543
+ const withSQL = this.with();
544
+ const wheres = this.where();
545
+ return (
546
+ withSQL +
547
+ `delete from ${this.single.only ? 'only ' : ''}${tableName}` +
548
+ (wheres ? ` ${wheres}` : '')
549
+ );
550
+ }
551
+
552
+ // Compiles a `truncate` query.
553
+ truncate() {
554
+ return `truncate ${this.tableName}`;
555
+ }
556
+
557
+ // Compiles the "locks".
558
+ lock() {
559
+ if (this.single.lock) {
560
+ return this[this.single.lock]();
561
+ }
562
+ }
563
+
564
+ // Compiles the wait mode on the locks.
565
+ waitMode() {
566
+ if (this.single.waitMode) {
567
+ return this[this.single.waitMode]();
568
+ }
569
+ }
570
+
571
+ // Fail on unsupported databases
572
+ skipLocked() {
573
+ throw new Error(
574
+ '.skipLocked() is currently only supported on MySQL 8.0+ and PostgreSQL 9.5+'
575
+ );
576
+ }
577
+
578
+ // Fail on unsupported databases
579
+ noWait() {
580
+ throw new Error(
581
+ '.noWait() is currently only supported on MySQL 8.0+, MariaDB 10.3.0+ and PostgreSQL 9.5+'
582
+ );
583
+ }
584
+
585
+ distinctOn(value) {
586
+ throw new Error('.distinctOn() is currently only supported on PostgreSQL');
587
+ }
588
+
589
+ // On Clause
590
+ // ------
591
+
592
+ onWrapped(clause) {
593
+ const self = this;
594
+
595
+ const wrapJoin = new JoinClause();
596
+ clause.value.call(wrapJoin, wrapJoin);
597
+
598
+ let sql = '';
599
+ wrapJoin.clauses.forEach(function (wrapClause, ii) {
600
+ if (ii > 0) {
601
+ sql += ` ${wrapClause.bool} `;
602
+ }
603
+ const val = self[wrapClause.type](wrapClause);
604
+ if (val) {
605
+ sql += val;
606
+ }
607
+ });
608
+
609
+ if (sql.length) {
610
+ return `(${sql})`;
611
+ }
612
+ return '';
613
+ }
614
+
615
+ onBasic(clause) {
616
+ return (
617
+ this.formatter.wrap(clause.column) +
618
+ ' ' +
619
+ this.formatter.operator(clause.operator) +
620
+ ' ' +
621
+ this.formatter.wrap(clause.value)
622
+ );
623
+ }
624
+
625
+ onVal(clause) {
626
+ return (
627
+ this.formatter.wrap(clause.column) +
628
+ ' ' +
629
+ this.formatter.operator(clause.operator) +
630
+ ' ' +
631
+ this.formatter.parameter(clause.value)
632
+ );
633
+ }
634
+
635
+ onRaw(clause) {
636
+ return this.formatter.unwrapRaw(clause.value);
637
+ }
638
+
639
+ onUsing(clause) {
640
+ return '(' + this.formatter.columnize(clause.column) + ')';
641
+ }
642
+
643
+ // Where Clause
644
+ // ------
645
+
646
+ whereIn(statement) {
647
+ let columns = null;
648
+ if (Array.isArray(statement.column)) {
649
+ columns = `(${this.formatter.columnize(statement.column)})`;
650
+ } else {
651
+ columns = this.formatter.wrap(statement.column);
652
+ }
653
+
654
+ const values = this.formatter.values(statement.value);
655
+ return `${columns} ${this._not(statement, 'in ')}${values}`;
656
+ }
657
+
658
+ whereNull(statement) {
659
+ return (
660
+ this.formatter.wrap(statement.column) +
661
+ ' is ' +
662
+ this._not(statement, 'null')
663
+ );
664
+ }
665
+
666
+ // Compiles a basic "where" clause.
667
+ whereBasic(statement) {
668
+ return (
669
+ this._not(statement, '') +
670
+ this.formatter.wrap(statement.column) +
671
+ ' ' +
672
+ this.formatter.operator(statement.operator) +
673
+ ' ' +
674
+ (statement.asColumn
675
+ ? this.formatter.wrap(statement.value)
676
+ : this.formatter.parameter(statement.value))
677
+ );
678
+ }
679
+
680
+ whereExists(statement) {
681
+ return (
682
+ this._not(statement, 'exists') +
683
+ ' (' +
684
+ this.formatter.rawOrFn(statement.value) +
685
+ ')'
686
+ );
687
+ }
688
+
689
+ whereWrapped(statement) {
690
+ const val = this.formatter.rawOrFn(statement.value, 'where');
691
+ return (val && this._not(statement, '') + '(' + val.slice(6) + ')') || '';
692
+ }
693
+
694
+ whereBetween(statement) {
695
+ return (
696
+ this.formatter.wrap(statement.column) +
697
+ ' ' +
698
+ this._not(statement, 'between') +
699
+ ' ' +
700
+ map(statement.value, bind(this.formatter.parameter, this.formatter)).join(
701
+ ' and '
702
+ )
703
+ );
704
+ }
705
+
706
+ // Compiles a "whereRaw" query.
707
+ whereRaw(statement) {
708
+ return this._not(statement, '') + this.formatter.unwrapRaw(statement.value);
709
+ }
710
+
711
+ wrap(str) {
712
+ if (str.charAt(0) !== '(') return `(${str})`;
713
+ return str;
714
+ }
715
+
716
+ // Compiles all `with` statements on the query.
717
+ with() {
718
+ if (!this.grouped.with || !this.grouped.with.length) {
719
+ return '';
720
+ }
721
+ const withs = this.grouped.with;
722
+ if (!withs) return;
723
+ const sql = [];
724
+ let i = -1;
725
+ let isRecursive = false;
726
+ while (++i < withs.length) {
727
+ const stmt = withs[i];
728
+ if (stmt.recursive) {
729
+ isRecursive = true;
730
+ }
731
+ const val = this[stmt.type](stmt);
732
+ sql.push(val);
733
+ }
734
+ return `with ${isRecursive ? 'recursive ' : ''}${sql.join(', ')} `;
735
+ }
736
+
737
+ withWrapped(statement) {
738
+ const val = this.formatter.rawOrFn(statement.value);
739
+ return (
740
+ (val &&
741
+ this.formatter.columnize(statement.alias) + ' as (' + val + ')') ||
742
+ ''
743
+ );
744
+ }
745
+
746
+ // Determines whether to add a "not" prefix to the where clause.
747
+ _not(statement, str) {
748
+ if (statement.not) return `not ${str}`;
749
+ return str;
750
+ }
751
+
752
+ _prepInsert(data) {
753
+ const isRaw = this.formatter.rawOrFn(data);
754
+ if (isRaw) return isRaw;
755
+ let columns = [];
756
+ const values = [];
757
+ if (!Array.isArray(data)) data = data ? [data] : [];
758
+ let i = -1;
759
+ while (++i < data.length) {
760
+ if (data[i] == null) break;
761
+ if (i === 0) columns = Object.keys(data[i]).sort();
762
+ const row = new Array(columns.length);
763
+ const keys = Object.keys(data[i]);
764
+ let j = -1;
765
+ while (++j < keys.length) {
766
+ const key = keys[j];
767
+ let idx = columns.indexOf(key);
768
+ if (idx === -1) {
769
+ columns = columns.concat(key).sort();
770
+ idx = columns.indexOf(key);
771
+ let k = -1;
772
+ while (++k < values.length) {
773
+ values[k].splice(idx, 0, undefined);
774
+ }
775
+ row.splice(idx, 0, undefined);
776
+ }
777
+ row[idx] = data[i][key];
778
+ }
779
+ values.push(row);
780
+ }
781
+ return {
782
+ columns,
783
+ values,
784
+ };
785
+ }
786
+
787
+ // "Preps" the update.
788
+ _prepUpdate(data = {}) {
789
+ const { counter = {} } = this.single;
790
+
791
+ for (const column of Object.keys(counter)) {
792
+ //Skip?
793
+ if (has(data, column)) {
794
+ //Needed?
795
+ this.client.logger.warn(
796
+ `increment/decrement called for a column that has already been specified in main .update() call. Ignoring increment/decrement and using value from .update() call.`
797
+ );
798
+ continue;
799
+ }
800
+
801
+ let value = counter[column];
802
+
803
+ const symbol = value < 0 ? '-' : '+';
804
+
805
+ if (symbol === '-') {
806
+ value = -value;
807
+ }
808
+
809
+ data[column] = this.client.raw(`?? ${symbol} ?`, [column, value]);
810
+ }
811
+
812
+ data = omitBy(data, isUndefined);
813
+
814
+ const vals = [];
815
+ const columns = Object.keys(data);
816
+ let i = -1;
817
+
818
+ while (++i < columns.length) {
819
+ vals.push(
820
+ this.formatter.wrap(columns[i]) +
821
+ ' = ' +
822
+ this.formatter.parameter(data[columns[i]])
823
+ );
824
+ }
825
+
826
+ if (isEmpty(vals)) {
827
+ throw new Error(
828
+ [
829
+ 'Empty .update() call detected!',
830
+ 'Update data does not contain any values to update.',
831
+ 'This will result in a faulty query.',
832
+ this.single.table ? `Table: ${this.single.table}.` : '',
833
+ this.single.update
834
+ ? `Columns: ${Object.keys(this.single.update)}.`
835
+ : '',
836
+ ].join(' ')
837
+ );
838
+ }
839
+
840
+ return vals;
841
+ }
842
+
843
+ _formatGroupsItemValue(value) {
844
+ const { formatter } = this;
845
+ if (value instanceof Raw) {
846
+ return formatter.unwrapRaw(value);
847
+ } else if (value instanceof QueryBuilder) {
848
+ return '(' + formatter.columnize(value) + ')';
849
+ } else {
850
+ return formatter.columnize(value);
851
+ }
852
+ }
853
+
854
+ // Compiles the `order by` statements.
855
+ _groupsOrders(type) {
856
+ const items = this.grouped[type];
857
+ if (!items) return '';
858
+ const { formatter } = this;
859
+ const sql = items.map((item) => {
860
+ const column = this._formatGroupsItemValue(item.value);
861
+ const direction =
862
+ type === 'order' && item.type !== 'orderByRaw'
863
+ ? ` ${formatter.direction(item.direction)}`
864
+ : '';
865
+ return column + direction;
866
+ });
867
+ return sql.length ? type + ' by ' + sql.join(', ') : '';
868
+ }
869
+
870
+ // Get the table name, wrapping it if necessary.
871
+ // Implemented as a property to prevent ordering issues as described in #704.
872
+ get tableName() {
873
+ if (!this._tableName) {
874
+ // Only call this.formatter.wrap() the first time this property is accessed.
875
+ let tableName = this.single.table;
876
+ const schemaName = this.single.schema;
877
+
878
+ if (tableName && schemaName) tableName = `${schemaName}.${tableName}`;
879
+
880
+ this._tableName = tableName
881
+ ? // Wrap subQuery with parenthesis, #3485
882
+ this.formatter.wrap(tableName, tableName instanceof QueryBuilder)
883
+ : '';
884
+ }
885
+ return this._tableName;
886
+ }
887
+ }
888
+
889
+ module.exports = QueryCompiler;