UNPKG

34.4 kBJavaScriptView Raw
1// Builder
2// -------
3const assert = require('assert');
4const inherits = require('inherits');
5const { EventEmitter } = require('events');
6
7const Raw = require('../raw');
8const helpers = require('../helpers');
9const JoinClause = require('./joinclause');
10const {
11 assign,
12 clone,
13 each,
14 isBoolean,
15 isEmpty,
16 isFunction,
17 isNil,
18 isNumber,
19 isObject,
20 isString,
21 isUndefined,
22 tail,
23 toArray,
24 reject,
25 includes,
26 last,
27 isPlainObject,
28} = require('lodash');
29const saveAsyncStack = require('../util/save-async-stack');
30
31const { lockMode, waitMode } = require('./constants');
32
33// Typically called from `knex.builder`,
34// start a new query building chain.
35function Builder(client) {
36 this.client = client;
37 this.and = this;
38 this._single = {};
39 this._statements = [];
40 this._method = 'select';
41 if (client.config) {
42 saveAsyncStack(this, 5);
43 this._debug = client.config.debug;
44 }
45 // Internal flags used in the builder.
46 this._joinFlag = 'inner';
47 this._boolFlag = 'and';
48 this._notFlag = false;
49 this._asColumnFlag = false;
50}
51
52inherits(Builder, EventEmitter);
53
54const validateWithArgs = function(alias, statement, method) {
55 if (typeof alias !== 'string') {
56 throw new Error(`${method}() first argument must be a string`);
57 }
58 if (
59 typeof statement === 'function' ||
60 statement instanceof Builder ||
61 statement instanceof Raw
62 ) {
63 return;
64 }
65 throw new Error(
66 `${method}() second argument must be a function / QueryBuilder or a raw`
67 );
68};
69
70assign(Builder.prototype, {
71 toString() {
72 return this.toQuery();
73 },
74
75 // Convert the current query "toSQL"
76 toSQL(method, tz) {
77 return this.client.queryCompiler(this).toSQL(method || this._method, tz);
78 },
79
80 // Create a shallow clone of the current query builder.
81 clone() {
82 const cloned = new this.constructor(this.client);
83 cloned._method = this._method;
84 cloned._single = clone(this._single);
85 cloned._statements = clone(this._statements);
86 cloned._debug = this._debug;
87
88 // `_option` is assigned by the `Interface` mixin.
89 if (!isUndefined(this._options)) {
90 cloned._options = clone(this._options);
91 }
92 if (!isUndefined(this._queryContext)) {
93 cloned._queryContext = clone(this._queryContext);
94 }
95 if (!isUndefined(this._connection)) {
96 cloned._connection = this._connection;
97 }
98
99 return cloned;
100 },
101
102 timeout(ms, { cancel } = {}) {
103 if (isNumber(ms) && ms > 0) {
104 this._timeout = ms;
105 if (cancel) {
106 this.client.assertCanCancelQuery();
107 this._cancelOnTimeout = true;
108 }
109 }
110 return this;
111 },
112
113 // With
114 // ------
115
116 with(alias, statement) {
117 validateWithArgs(alias, statement, 'with');
118 return this.withWrapped(alias, statement);
119 },
120
121 // Helper for compiling any advanced `with` queries.
122 withWrapped(alias, query) {
123 this._statements.push({
124 grouping: 'with',
125 type: 'withWrapped',
126 alias: alias,
127 value: query,
128 });
129 return this;
130 },
131
132 // With Recursive
133 // ------
134
135 withRecursive(alias, statement) {
136 validateWithArgs(alias, statement, 'withRecursive');
137 return this.withRecursiveWrapped(alias, statement);
138 },
139
140 // Helper for compiling any advanced `withRecursive` queries.
141 withRecursiveWrapped(alias, query) {
142 this.withWrapped(alias, query);
143 this._statements[this._statements.length - 1].recursive = true;
144 return this;
145 },
146
147 // Select
148 // ------
149
150 // Adds a column or columns to the list of "columns"
151 // being selected on the query.
152 columns(column) {
153 if (!column && column !== 0) return this;
154 this._statements.push({
155 grouping: 'columns',
156 value: helpers.normalizeArr.apply(null, arguments),
157 });
158 return this;
159 },
160
161 // Allow for a sub-select to be explicitly aliased as a column,
162 // without needing to compile the query in a where.
163 as(column) {
164 this._single.as = column;
165 return this;
166 },
167
168 // Prepends the `schemaName` on `tableName` defined by `.table` and `.join`.
169 withSchema(schemaName) {
170 this._single.schema = schemaName;
171 return this;
172 },
173
174 // Sets the `tableName` on the query.
175 // Alias to "from" for select and "into" for insert statements
176 // e.g. builder.insert({a: value}).into('tableName')
177 // `options`: options object containing keys:
178 // - `only`: whether the query should use SQL's ONLY to not return
179 // inheriting table data. Defaults to false.
180 table(tableName, options = {}) {
181 this._single.table = tableName;
182 this._single.only = options.only === true;
183 return this;
184 },
185
186 // Adds a `distinct` clause to the query.
187 distinct() {
188 this._statements.push({
189 grouping: 'columns',
190 value: helpers.normalizeArr.apply(null, arguments),
191 distinct: true,
192 });
193 return this;
194 },
195
196 distinctOn() {
197 const value = helpers.normalizeArr.apply(null, arguments);
198 if (isEmpty(value)) {
199 throw new Error('distinctOn requires atleast on argument');
200 }
201 this._statements.push({
202 grouping: 'columns',
203 value,
204 distinctOn: true,
205 });
206 return this;
207 },
208
209 // Adds a join clause to the query, allowing for advanced joins
210 // with an anonymous function as the second argument.
211 // function(table, first, operator, second)
212 join(table, first) {
213 let join;
214 const { schema } = this._single;
215 const joinType = this._joinType();
216 if (typeof first === 'function') {
217 join = new JoinClause(table, joinType, schema);
218 first.call(join, join);
219 } else if (joinType === 'raw') {
220 join = new JoinClause(this.client.raw(table, first), 'raw');
221 } else {
222 join = new JoinClause(
223 table,
224 joinType,
225 table instanceof Builder ? undefined : schema
226 );
227 if (arguments.length > 1) {
228 join.on.apply(join, toArray(arguments).slice(1));
229 }
230 }
231 this._statements.push(join);
232 return this;
233 },
234
235 // JOIN blocks:
236 innerJoin() {
237 return this._joinType('inner').join.apply(this, arguments);
238 },
239 leftJoin() {
240 return this._joinType('left').join.apply(this, arguments);
241 },
242 leftOuterJoin() {
243 return this._joinType('left outer').join.apply(this, arguments);
244 },
245 rightJoin() {
246 return this._joinType('right').join.apply(this, arguments);
247 },
248 rightOuterJoin() {
249 return this._joinType('right outer').join.apply(this, arguments);
250 },
251 outerJoin() {
252 return this._joinType('outer').join.apply(this, arguments);
253 },
254 fullOuterJoin() {
255 return this._joinType('full outer').join.apply(this, arguments);
256 },
257 crossJoin() {
258 return this._joinType('cross').join.apply(this, arguments);
259 },
260 joinRaw() {
261 return this._joinType('raw').join.apply(this, arguments);
262 },
263
264 // The where function can be used in several ways:
265 // The most basic is `where(key, value)`, which expands to
266 // where key = value.
267 where(column, operator, value) {
268 // Support "where true || where false"
269 if (column === false || column === true) {
270 return this.where(1, '=', column ? 1 : 0);
271 }
272
273 // Check if the column is a function, in which case it's
274 // a where statement wrapped in parens.
275 if (typeof column === 'function') {
276 return this.whereWrapped(column);
277 }
278
279 // Allow a raw statement to be passed along to the query.
280 if (column instanceof Raw && arguments.length === 1)
281 return this.whereRaw(column);
282
283 // Allows `where({id: 2})` syntax.
284 if (isObject(column) && !(column instanceof Raw))
285 return this._objectWhere(column);
286
287 // Enable the where('key', value) syntax, only when there
288 // are explicitly two arguments passed, so it's not possible to
289 // do where('key', '!=') and have that turn into where key != null
290 if (arguments.length === 2) {
291 value = operator;
292 operator = '=';
293
294 // If the value is null, and it's a two argument query,
295 // we assume we're going for a `whereNull`.
296 if (value === null) {
297 return this.whereNull(column);
298 }
299 }
300
301 // lower case the operator for comparison purposes
302 const checkOperator = `${operator}`.toLowerCase().trim();
303
304 // If there are 3 arguments, check whether 'in' is one of them.
305 if (arguments.length === 3) {
306 if (checkOperator === 'in' || checkOperator === 'not in') {
307 return this._not(checkOperator === 'not in').whereIn(
308 arguments[0],
309 arguments[2]
310 );
311 }
312 if (checkOperator === 'between' || checkOperator === 'not between') {
313 return this._not(checkOperator === 'not between').whereBetween(
314 arguments[0],
315 arguments[2]
316 );
317 }
318 }
319
320 // If the value is still null, check whether they're meaning
321 // where value is null
322 if (value === null) {
323 // Check for .where(key, 'is', null) or .where(key, 'is not', 'null');
324 if (checkOperator === 'is' || checkOperator === 'is not') {
325 return this._not(checkOperator === 'is not').whereNull(column);
326 }
327 }
328
329 // Push onto the where statement stack.
330 this._statements.push({
331 grouping: 'where',
332 type: 'whereBasic',
333 column,
334 operator,
335 value,
336 not: this._not(),
337 bool: this._bool(),
338 asColumn: this._asColumnFlag,
339 });
340 return this;
341 },
342
343 whereColumn(column, operator, rightColumn) {
344 this._asColumnFlag = true;
345 this.where.apply(this, arguments);
346 this._asColumnFlag = false;
347 return this;
348 },
349
350 // Adds an `or where` clause to the query.
351 orWhere() {
352 this._bool('or');
353 const obj = arguments[0];
354 if (isObject(obj) && !isFunction(obj) && !(obj instanceof Raw)) {
355 return this.whereWrapped(function() {
356 for (const key in obj) {
357 this.andWhere(key, obj[key]);
358 }
359 });
360 }
361 return this.where.apply(this, arguments);
362 },
363
364 orWhereColumn() {
365 this._bool('or');
366 const obj = arguments[0];
367 if (isObject(obj) && !isFunction(obj) && !(obj instanceof Raw)) {
368 return this.whereWrapped(function() {
369 for (const key in obj) {
370 this.andWhereColumn(key, '=', obj[key]);
371 }
372 });
373 }
374 return this.whereColumn.apply(this, arguments);
375 },
376
377 // Adds an `not where` clause to the query.
378 whereNot() {
379 return this._not(true).where.apply(this, arguments);
380 },
381
382 whereNotColumn() {
383 return this._not(true).whereColumn.apply(this, arguments);
384 },
385
386 // Adds an `or not where` clause to the query.
387 orWhereNot() {
388 return this._bool('or').whereNot.apply(this, arguments);
389 },
390
391 orWhereNotColumn() {
392 return this._bool('or').whereNotColumn.apply(this, arguments);
393 },
394
395 // Processes an object literal provided in a "where" clause.
396 _objectWhere(obj) {
397 const boolVal = this._bool();
398 const notVal = this._not() ? 'Not' : '';
399 for (const key in obj) {
400 this[boolVal + 'Where' + notVal](key, obj[key]);
401 }
402 return this;
403 },
404
405 // Adds a raw `where` clause to the query.
406 whereRaw(sql, bindings) {
407 const raw = sql instanceof Raw ? sql : this.client.raw(sql, bindings);
408 this._statements.push({
409 grouping: 'where',
410 type: 'whereRaw',
411 value: raw,
412 not: this._not(),
413 bool: this._bool(),
414 });
415 return this;
416 },
417
418 orWhereRaw(sql, bindings) {
419 return this._bool('or').whereRaw(sql, bindings);
420 },
421
422 // Helper for compiling any advanced `where` queries.
423 whereWrapped(callback) {
424 this._statements.push({
425 grouping: 'where',
426 type: 'whereWrapped',
427 value: callback,
428 not: this._not(),
429 bool: this._bool(),
430 });
431 return this;
432 },
433
434 // Adds a `where exists` clause to the query.
435 whereExists(callback) {
436 this._statements.push({
437 grouping: 'where',
438 type: 'whereExists',
439 value: callback,
440 not: this._not(),
441 bool: this._bool(),
442 });
443 return this;
444 },
445
446 // Adds an `or where exists` clause to the query.
447 orWhereExists(callback) {
448 return this._bool('or').whereExists(callback);
449 },
450
451 // Adds a `where not exists` clause to the query.
452 whereNotExists(callback) {
453 return this._not(true).whereExists(callback);
454 },
455
456 // Adds a `or where not exists` clause to the query.
457 orWhereNotExists(callback) {
458 return this._bool('or').whereNotExists(callback);
459 },
460
461 // Adds a `where in` clause to the query.
462 whereIn(column, values) {
463 if (Array.isArray(values) && isEmpty(values))
464 return this.where(this._not());
465 this._statements.push({
466 grouping: 'where',
467 type: 'whereIn',
468 column,
469 value: values,
470 not: this._not(),
471 bool: this._bool(),
472 });
473 return this;
474 },
475
476 // Adds a `or where in` clause to the query.
477 orWhereIn(column, values) {
478 return this._bool('or').whereIn(column, values);
479 },
480
481 // Adds a `where not in` clause to the query.
482 whereNotIn(column, values) {
483 return this._not(true).whereIn(column, values);
484 },
485
486 // Adds a `or where not in` clause to the query.
487 orWhereNotIn(column, values) {
488 return this._bool('or')
489 ._not(true)
490 .whereIn(column, values);
491 },
492
493 // Adds a `where null` clause to the query.
494 whereNull(column) {
495 this._statements.push({
496 grouping: 'where',
497 type: 'whereNull',
498 column,
499 not: this._not(),
500 bool: this._bool(),
501 });
502 return this;
503 },
504
505 // Adds a `or where null` clause to the query.
506 orWhereNull(column) {
507 return this._bool('or').whereNull(column);
508 },
509
510 // Adds a `where not null` clause to the query.
511 whereNotNull(column) {
512 return this._not(true).whereNull(column);
513 },
514
515 // Adds a `or where not null` clause to the query.
516 orWhereNotNull(column) {
517 return this._bool('or').whereNotNull(column);
518 },
519
520 // Adds a `where between` clause to the query.
521 whereBetween(column, values) {
522 assert(
523 Array.isArray(values),
524 'The second argument to whereBetween must be an array.'
525 );
526 assert(
527 values.length === 2,
528 'You must specify 2 values for the whereBetween clause'
529 );
530 this._statements.push({
531 grouping: 'where',
532 type: 'whereBetween',
533 column,
534 value: values,
535 not: this._not(),
536 bool: this._bool(),
537 });
538 return this;
539 },
540
541 // Adds a `where not between` clause to the query.
542 whereNotBetween(column, values) {
543 return this._not(true).whereBetween(column, values);
544 },
545
546 // Adds a `or where between` clause to the query.
547 orWhereBetween(column, values) {
548 return this._bool('or').whereBetween(column, values);
549 },
550
551 // Adds a `or where not between` clause to the query.
552 orWhereNotBetween(column, values) {
553 return this._bool('or').whereNotBetween(column, values);
554 },
555
556 // Adds a `group by` clause to the query.
557 groupBy(item) {
558 if (item instanceof Raw) {
559 return this.groupByRaw.apply(this, arguments);
560 }
561 this._statements.push({
562 grouping: 'group',
563 type: 'groupByBasic',
564 value: helpers.normalizeArr.apply(null, arguments),
565 });
566 return this;
567 },
568
569 // Adds a raw `group by` clause to the query.
570 groupByRaw(sql, bindings) {
571 const raw = sql instanceof Raw ? sql : this.client.raw(sql, bindings);
572 this._statements.push({
573 grouping: 'group',
574 type: 'groupByRaw',
575 value: raw,
576 });
577 return this;
578 },
579
580 // Adds a `order by` clause to the query.
581 orderBy(column, direction) {
582 if (Array.isArray(column)) {
583 return this._orderByArray(column);
584 }
585 this._statements.push({
586 grouping: 'order',
587 type: 'orderByBasic',
588 value: column,
589 direction,
590 });
591 return this;
592 },
593
594 // Adds a `order by` with multiple columns to the query.
595 _orderByArray(columnDefs) {
596 for (let i = 0; i < columnDefs.length; i++) {
597 const columnInfo = columnDefs[i];
598 if (isObject(columnInfo)) {
599 this._statements.push({
600 grouping: 'order',
601 type: 'orderByBasic',
602 value: columnInfo['column'],
603 direction: columnInfo['order'],
604 });
605 } else if (isString(columnInfo)) {
606 this._statements.push({
607 grouping: 'order',
608 type: 'orderByBasic',
609 value: columnInfo,
610 });
611 }
612 }
613 return this;
614 },
615
616 // Add a raw `order by` clause to the query.
617 orderByRaw(sql, bindings) {
618 const raw = sql instanceof Raw ? sql : this.client.raw(sql, bindings);
619 this._statements.push({
620 grouping: 'order',
621 type: 'orderByRaw',
622 value: raw,
623 });
624 return this;
625 },
626
627 _union(clause, args) {
628 let callbacks = args[0];
629 let wrap = args[1];
630 if (args.length === 1 || (args.length === 2 && isBoolean(wrap))) {
631 if (!Array.isArray(callbacks)) {
632 callbacks = [callbacks];
633 }
634 for (let i = 0, l = callbacks.length; i < l; i++) {
635 this._statements.push({
636 grouping: 'union',
637 clause: clause,
638 value: callbacks[i],
639 wrap: wrap || false,
640 });
641 }
642 } else {
643 callbacks = toArray(args).slice(0, args.length - 1);
644 wrap = args[args.length - 1];
645 if (!isBoolean(wrap)) {
646 callbacks.push(wrap);
647 wrap = false;
648 }
649 this._union(clause, [callbacks, wrap]);
650 }
651 return this;
652 },
653
654 // Add a union statement to the query.
655 union(...args) {
656 return this._union('union', args);
657 },
658
659 // Adds a union all statement to the query.
660 unionAll(...args) {
661 return this._union('union all', args);
662 },
663
664 // Adds an intersect statement to the query
665 intersect(callbacks, wrap) {
666 if (arguments.length === 1 || (arguments.length === 2 && isBoolean(wrap))) {
667 if (!Array.isArray(callbacks)) {
668 callbacks = [callbacks];
669 }
670 for (let i = 0, l = callbacks.length; i < l; i++) {
671 this._statements.push({
672 grouping: 'union',
673 clause: 'intersect',
674 value: callbacks[i],
675 wrap: wrap || false,
676 });
677 }
678 } else {
679 callbacks = toArray(arguments).slice(0, arguments.length - 1);
680 wrap = arguments[arguments.length - 1];
681 if (!isBoolean(wrap)) {
682 callbacks.push(wrap);
683 wrap = false;
684 }
685 this.intersect(callbacks, wrap);
686 }
687 return this;
688 },
689
690 // Adds a `having` clause to the query.
691 having(column, operator, value) {
692 if (column instanceof Raw && arguments.length === 1) {
693 return this.havingRaw(column);
694 }
695
696 // Check if the column is a function, in which case it's
697 // a having statement wrapped in parens.
698 if (typeof column === 'function') {
699 return this.havingWrapped(column);
700 }
701
702 this._statements.push({
703 grouping: 'having',
704 type: 'havingBasic',
705 column,
706 operator,
707 value,
708 bool: this._bool(),
709 not: this._not(),
710 });
711 return this;
712 },
713
714 orHaving: function orHaving() {
715 this._bool('or');
716 const obj = arguments[0];
717 if (isObject(obj) && !isFunction(obj) && !(obj instanceof Raw)) {
718 return this.havingWrapped(function() {
719 for (const key in obj) {
720 this.andHaving(key, obj[key]);
721 }
722 });
723 }
724 return this.having.apply(this, arguments);
725 },
726
727 // Helper for compiling any advanced `having` queries.
728 havingWrapped(callback) {
729 this._statements.push({
730 grouping: 'having',
731 type: 'havingWrapped',
732 value: callback,
733 bool: this._bool(),
734 not: this._not(),
735 });
736 return this;
737 },
738
739 havingNull(column) {
740 this._statements.push({
741 grouping: 'having',
742 type: 'havingNull',
743 column,
744 not: this._not(),
745 bool: this._bool(),
746 });
747 return this;
748 },
749
750 orHavingNull(callback) {
751 return this._bool('or').havingNull(callback);
752 },
753
754 havingNotNull(callback) {
755 return this._not(true).havingNull(callback);
756 },
757
758 orHavingNotNull(callback) {
759 return this._not(true)
760 ._bool('or')
761 .havingNull(callback);
762 },
763
764 havingExists(callback) {
765 this._statements.push({
766 grouping: 'having',
767 type: 'havingExists',
768 value: callback,
769 not: this._not(),
770 bool: this._bool(),
771 });
772 return this;
773 },
774
775 orHavingExists(callback) {
776 return this._bool('or').havingExists(callback);
777 },
778
779 havingNotExists(callback) {
780 return this._not(true).havingExists(callback);
781 },
782
783 orHavingNotExists(callback) {
784 return this._not(true)
785 ._bool('or')
786 .havingExists(callback);
787 },
788
789 havingBetween(column, values) {
790 assert(
791 Array.isArray(values),
792 'The second argument to havingBetween must be an array.'
793 );
794 assert(
795 values.length === 2,
796 'You must specify 2 values for the havingBetween clause'
797 );
798 this._statements.push({
799 grouping: 'having',
800 type: 'havingBetween',
801 column,
802 value: values,
803 not: this._not(),
804 bool: this._bool(),
805 });
806 return this;
807 },
808
809 orHavingBetween(column, values) {
810 return this._bool('or').havingBetween(column, values);
811 },
812
813 havingNotBetween(column, values) {
814 return this._not(true).havingBetween(column, values);
815 },
816
817 orHavingNotBetween(column, values) {
818 return this._not(true)
819 ._bool('or')
820 .havingBetween(column, values);
821 },
822
823 havingIn(column, values) {
824 if (Array.isArray(values) && isEmpty(values))
825 return this.where(this._not());
826 this._statements.push({
827 grouping: 'having',
828 type: 'havingIn',
829 column,
830 value: values,
831 not: this._not(),
832 bool: this._bool(),
833 });
834 return this;
835 },
836
837 // Adds a `or where in` clause to the query.
838 orHavingIn(column, values) {
839 return this._bool('or').havingIn(column, values);
840 },
841
842 // Adds a `where not in` clause to the query.
843 havingNotIn(column, values) {
844 return this._not(true).havingIn(column, values);
845 },
846
847 // Adds a `or where not in` clause to the query.
848 orHavingNotIn(column, values) {
849 return this._bool('or')
850 ._not(true)
851 .havingIn(column, values);
852 },
853
854 // Adds a raw `having` clause to the query.
855 havingRaw(sql, bindings) {
856 const raw = sql instanceof Raw ? sql : this.client.raw(sql, bindings);
857 this._statements.push({
858 grouping: 'having',
859 type: 'havingRaw',
860 value: raw,
861 bool: this._bool(),
862 not: this._not(),
863 });
864 return this;
865 },
866
867 orHavingRaw(sql, bindings) {
868 return this._bool('or').havingRaw(sql, bindings);
869 },
870
871 // Only allow a single "offset" to be set for the current query.
872 offset(value) {
873 if (isNil(value) || value instanceof Raw || value instanceof Builder) {
874 // Builder for backward compatibility
875 this._single.offset = value;
876 } else {
877 const val = parseInt(value, 10);
878 if (isNaN(val)) {
879 this.client.logger.warn('A valid integer must be provided to offset');
880 } else {
881 this._single.offset = val;
882 }
883 }
884 return this;
885 },
886
887 // Only allow a single "limit" to be set for the current query.
888 limit(value) {
889 const val = parseInt(value, 10);
890 if (isNaN(val)) {
891 this.client.logger.warn('A valid integer must be provided to limit');
892 } else {
893 this._single.limit = val;
894 }
895 return this;
896 },
897
898 // Retrieve the "count" result of the query.
899 count(column, options) {
900 return this._aggregate('count', column || '*', options);
901 },
902
903 // Retrieve the minimum value of a given column.
904 min(column, options) {
905 return this._aggregate('min', column, options);
906 },
907
908 // Retrieve the maximum value of a given column.
909 max(column, options) {
910 return this._aggregate('max', column, options);
911 },
912
913 // Retrieve the sum of the values of a given column.
914 sum(column, options) {
915 return this._aggregate('sum', column, options);
916 },
917
918 // Retrieve the average of the values of a given column.
919 avg(column, options) {
920 return this._aggregate('avg', column, options);
921 },
922
923 // Retrieve the "count" of the distinct results of the query.
924 countDistinct() {
925 let columns = helpers.normalizeArr.apply(null, arguments);
926 let options;
927 if (columns.length > 1 && isPlainObject(last(columns))) {
928 [options] = columns.splice(columns.length - 1, 1);
929 }
930
931 if (!columns.length) {
932 columns = '*';
933 } else if (columns.length === 1) {
934 columns = columns[0];
935 }
936
937 return this._aggregate('count', columns, { ...options, distinct: true });
938 },
939
940 // Retrieve the sum of the distinct values of a given column.
941 sumDistinct(column, options) {
942 return this._aggregate('sum', column, { ...options, distinct: true });
943 },
944
945 // Retrieve the vg of the distinct results of the query.
946 avgDistinct(column, options) {
947 return this._aggregate('avg', column, { ...options, distinct: true });
948 },
949
950 // Increments a column's value by the specified amount.
951 increment(column, amount = 1) {
952 if (isObject(column)) {
953 for (const key in column) {
954 this._counter(key, column[key]);
955 }
956
957 return this;
958 }
959
960 return this._counter(column, amount);
961 },
962
963 // Decrements a column's value by the specified amount.
964 decrement(column, amount = 1) {
965 if (isObject(column)) {
966 for (const key in column) {
967 this._counter(key, -column[key]);
968 }
969
970 return this;
971 }
972
973 return this._counter(column, -amount);
974 },
975
976 // Clears increments/decrements
977 clearCounters() {
978 this._single.counter = {};
979
980 return this;
981 },
982
983 // Sets the values for a `select` query, informing that only the first
984 // row should be returned (limit 1).
985 first() {
986 if (!this._isSelectQuery()) {
987 throw new Error(`Cannot chain .first() on "${this._method}" query!`);
988 }
989
990 const args = new Array(arguments.length);
991 for (let i = 0; i < args.length; i++) {
992 args[i] = arguments[i];
993 }
994 this.select.apply(this, args);
995 this._method = 'first';
996 this.limit(1);
997 return this;
998 },
999
1000 // Use existing connection to execute the query
1001 // Same value that client.acquireConnection() for an according client returns should be passed
1002 connection(_connection) {
1003 this._connection = _connection;
1004 return this;
1005 },
1006
1007 // Pluck a column from a query.
1008 pluck(column) {
1009 this._method = 'pluck';
1010 this._single.pluck = column;
1011 this._statements.push({
1012 grouping: 'columns',
1013 type: 'pluck',
1014 value: column,
1015 });
1016 return this;
1017 },
1018
1019 // Remove everything from select clause
1020 clearSelect() {
1021 this._clearGrouping('columns');
1022 return this;
1023 },
1024
1025 // Remove everything from where clause
1026 clearWhere() {
1027 this._clearGrouping('where');
1028 return this;
1029 },
1030
1031 // Remove everything from order clause
1032 clearOrder() {
1033 this._clearGrouping('order');
1034 return this;
1035 },
1036
1037 // Remove everything from having clause
1038 clearHaving() {
1039 this._clearGrouping('having');
1040 return this;
1041 },
1042
1043 // Insert & Update
1044 // ------
1045
1046 // Sets the values for an `insert` query.
1047 insert(values, returning) {
1048 this._method = 'insert';
1049 if (!isEmpty(returning)) this.returning(returning);
1050 this._single.insert = values;
1051 return this;
1052 },
1053
1054 // Sets the values for an `update`, allowing for both
1055 // `.update(key, value, [returning])` and `.update(obj, [returning])` syntaxes.
1056 update(values, returning) {
1057 let ret;
1058 const obj = this._single.update || {};
1059 this._method = 'update';
1060 if (isString(values)) {
1061 obj[values] = returning;
1062 if (arguments.length > 2) {
1063 ret = arguments[2];
1064 }
1065 } else {
1066 const keys = Object.keys(values);
1067 if (this._single.update) {
1068 this.client.logger.warn('Update called multiple times with objects.');
1069 }
1070 let i = -1;
1071 while (++i < keys.length) {
1072 obj[keys[i]] = values[keys[i]];
1073 }
1074 ret = arguments[1];
1075 }
1076 if (!isEmpty(ret)) this.returning(ret);
1077 this._single.update = obj;
1078 return this;
1079 },
1080
1081 // Sets the returning value for the query.
1082 returning(returning) {
1083 this._single.returning = returning;
1084 return this;
1085 },
1086
1087 // Delete
1088 // ------
1089
1090 // Executes a delete statement on the query;
1091 delete(ret) {
1092 this._method = 'del';
1093 if (!isEmpty(ret)) this.returning(ret);
1094 return this;
1095 },
1096
1097 // Truncates a table, ends the query chain.
1098 truncate(tableName) {
1099 this._method = 'truncate';
1100 if (tableName) {
1101 this._single.table = tableName;
1102 }
1103 return this;
1104 },
1105
1106 // Retrieves columns for the table specified by `knex(tableName)`
1107 columnInfo(column) {
1108 this._method = 'columnInfo';
1109 this._single.columnInfo = column;
1110 return this;
1111 },
1112
1113 // Set a lock for update constraint.
1114 forUpdate() {
1115 this._single.lock = lockMode.forUpdate;
1116 this._single.lockTables = helpers.normalizeArr.apply(null, arguments);
1117 return this;
1118 },
1119
1120 // Set a lock for share constraint.
1121 forShare() {
1122 this._single.lock = lockMode.forShare;
1123 this._single.lockTables = helpers.normalizeArr.apply(null, arguments);
1124 return this;
1125 },
1126
1127 // Skips locked rows when using a lock constraint.
1128 skipLocked() {
1129 if (!this._isSelectQuery()) {
1130 throw new Error(`Cannot chain .skipLocked() on "${this._method}" query!`);
1131 }
1132 if (!this._hasLockMode()) {
1133 throw new Error(
1134 '.skipLocked() can only be used after a call to .forShare() or .forUpdate()!'
1135 );
1136 }
1137 if (this._single.waitMode === waitMode.noWait) {
1138 throw new Error('.skipLocked() cannot be used together with .noWait()!');
1139 }
1140 this._single.waitMode = waitMode.skipLocked;
1141 return this;
1142 },
1143
1144 // Causes error when acessing a locked row instead of waiting for it to be released.
1145 noWait() {
1146 if (!this._isSelectQuery()) {
1147 throw new Error(`Cannot chain .noWait() on "${this._method}" query!`);
1148 }
1149 if (!this._hasLockMode()) {
1150 throw new Error(
1151 '.noWait() can only be used after a call to .forShare() or .forUpdate()!'
1152 );
1153 }
1154 if (this._single.waitMode === waitMode.skipLocked) {
1155 throw new Error('.noWait() cannot be used together with .skipLocked()!');
1156 }
1157 this._single.waitMode = waitMode.noWait;
1158 return this;
1159 },
1160
1161 // Takes a JS object of methods to call and calls them
1162 fromJS(obj) {
1163 each(obj, (val, key) => {
1164 if (typeof this[key] !== 'function') {
1165 this.client.logger.warn(`Knex Error: unknown key ${key}`);
1166 }
1167 if (Array.isArray(val)) {
1168 this[key].apply(this, val);
1169 } else {
1170 this[key](val);
1171 }
1172 });
1173 return this;
1174 },
1175
1176 // Passes query to provided callback function, useful for e.g. composing
1177 // domain-specific helpers
1178 modify(callback) {
1179 callback.apply(this, [this].concat(tail(arguments)));
1180 return this;
1181 },
1182
1183 // ----------------------------------------------------------------------
1184
1185 // Helper for the incrementing/decrementing queries.
1186 _counter(column, amount) {
1187 amount = parseFloat(amount);
1188
1189 this._method = 'update';
1190
1191 this._single.counter = this._single.counter || {};
1192
1193 this._single.counter[column] = amount;
1194
1195 return this;
1196 },
1197
1198 // Helper to get or set the "boolFlag" value.
1199 _bool(val) {
1200 if (arguments.length === 1) {
1201 this._boolFlag = val;
1202 return this;
1203 }
1204 const ret = this._boolFlag;
1205 this._boolFlag = 'and';
1206 return ret;
1207 },
1208
1209 // Helper to get or set the "notFlag" value.
1210 _not(val) {
1211 if (arguments.length === 1) {
1212 this._notFlag = val;
1213 return this;
1214 }
1215 const ret = this._notFlag;
1216 this._notFlag = false;
1217 return ret;
1218 },
1219
1220 // Helper to get or set the "joinFlag" value.
1221 _joinType(val) {
1222 if (arguments.length === 1) {
1223 this._joinFlag = val;
1224 return this;
1225 }
1226 const ret = this._joinFlag || 'inner';
1227 this._joinFlag = 'inner';
1228 return ret;
1229 },
1230
1231 // Helper for compiling any aggregate queries.
1232 _aggregate(method, column, options = {}) {
1233 this._statements.push({
1234 grouping: 'columns',
1235 type: column instanceof Raw ? 'aggregateRaw' : 'aggregate',
1236 method,
1237 value: column,
1238 aggregateDistinct: options.distinct || false,
1239 alias: options.as,
1240 });
1241 return this;
1242 },
1243
1244 // Helper function for clearing or reseting a grouping type from the builder
1245 _clearGrouping(grouping) {
1246 this._statements = reject(this._statements, { grouping });
1247 },
1248
1249 // Helper function that checks if the builder will emit a select query
1250 _isSelectQuery() {
1251 return includes(['pluck', 'first', 'select'], this._method);
1252 },
1253
1254 // Helper function that checks if the query has a lock mode set
1255 _hasLockMode() {
1256 return includes([lockMode.forShare, lockMode.forUpdate], this._single.lock);
1257 },
1258});
1259
1260Object.defineProperty(Builder.prototype, 'or', {
1261 get() {
1262 return this._bool('or');
1263 },
1264});
1265
1266Object.defineProperty(Builder.prototype, 'not', {
1267 get() {
1268 return this._not(true);
1269 },
1270});
1271
1272Builder.prototype.select = Builder.prototype.columns;
1273Builder.prototype.column = Builder.prototype.columns;
1274Builder.prototype.andWhereNot = Builder.prototype.whereNot;
1275Builder.prototype.andWhereNotColumn = Builder.prototype.whereNotColumn;
1276Builder.prototype.andWhere = Builder.prototype.where;
1277Builder.prototype.andWhereColumn = Builder.prototype.whereColumn;
1278Builder.prototype.andWhereRaw = Builder.prototype.whereRaw;
1279Builder.prototype.andWhereBetween = Builder.prototype.whereBetween;
1280Builder.prototype.andWhereNotBetween = Builder.prototype.whereNotBetween;
1281Builder.prototype.andHaving = Builder.prototype.having;
1282Builder.prototype.andHavingIn = Builder.prototype.havingIn;
1283Builder.prototype.andHavingNotIn = Builder.prototype.havingNotIn;
1284Builder.prototype.andHavingNull = Builder.prototype.havingNull;
1285Builder.prototype.andHavingNotNull = Builder.prototype.havingNotNull;
1286Builder.prototype.andHavingExists = Builder.prototype.havingExists;
1287Builder.prototype.andHavingNotExists = Builder.prototype.havingNotExists;
1288Builder.prototype.andHavingBetween = Builder.prototype.havingBetween;
1289Builder.prototype.andHavingNotBetween = Builder.prototype.havingNotBetween;
1290Builder.prototype.from = Builder.prototype.table;
1291Builder.prototype.into = Builder.prototype.table;
1292Builder.prototype.del = Builder.prototype.delete;
1293
1294// Attach all of the top level promise methods that should be chainable.
1295require('../interface')(Builder);
1296helpers.addQueryContext(Builder);
1297
1298Builder.extend = (methodName, fn) => {
1299 if (Object.prototype.hasOwnProperty.call(Builder.prototype, methodName)) {
1300 throw new Error(
1301 `Can't extend QueryBuilder with existing method ('${methodName}').`
1302 );
1303 }
1304
1305 assign(Builder.prototype, { [methodName]: fn });
1306};
1307
1308module.exports = Builder;