knex 3.2.3 → 3.2.4

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