1 |
|
2 |
|
3 | const assert = require('assert');
|
4 | const inherits = require('inherits');
|
5 | const { EventEmitter } = require('events');
|
6 |
|
7 | const Raw = require('../raw');
|
8 | const helpers = require('../helpers');
|
9 | const JoinClause = require('./joinclause');
|
10 | const {
|
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');
|
29 | const saveAsyncStack = require('../util/save-async-stack');
|
30 |
|
31 | const { lockMode, waitMode } = require('./constants');
|
32 |
|
33 |
|
34 |
|
35 | function 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 |
|
46 | this._joinFlag = 'inner';
|
47 | this._boolFlag = 'and';
|
48 | this._notFlag = false;
|
49 | this._asColumnFlag = false;
|
50 | }
|
51 |
|
52 | inherits(Builder, EventEmitter);
|
53 |
|
54 | const 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 |
|
70 | assign(Builder.prototype, {
|
71 | toString() {
|
72 | return this.toQuery();
|
73 | },
|
74 |
|
75 |
|
76 | toSQL(method, tz) {
|
77 | return this.client.queryCompiler(this).toSQL(method || this._method, tz);
|
78 | },
|
79 |
|
80 |
|
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 |
|
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 |
|
114 |
|
115 |
|
116 | with(alias, statement) {
|
117 | validateWithArgs(alias, statement, 'with');
|
118 | return this.withWrapped(alias, statement);
|
119 | },
|
120 |
|
121 |
|
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 |
|
133 |
|
134 |
|
135 | withRecursive(alias, statement) {
|
136 | validateWithArgs(alias, statement, 'withRecursive');
|
137 | return this.withRecursiveWrapped(alias, statement);
|
138 | },
|
139 |
|
140 |
|
141 | withRecursiveWrapped(alias, query) {
|
142 | this.withWrapped(alias, query);
|
143 | this._statements[this._statements.length - 1].recursive = true;
|
144 | return this;
|
145 | },
|
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
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 |
|
162 |
|
163 | as(column) {
|
164 | this._single.as = column;
|
165 | return this;
|
166 | },
|
167 |
|
168 |
|
169 | withSchema(schemaName) {
|
170 | this._single.schema = schemaName;
|
171 | return this;
|
172 | },
|
173 |
|
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 | table(tableName, options = {}) {
|
181 | this._single.table = tableName;
|
182 | this._single.only = options.only === true;
|
183 | return this;
|
184 | },
|
185 |
|
186 |
|
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 |
|
210 |
|
211 |
|
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 |
|
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 |
|
265 |
|
266 |
|
267 | where(column, operator, value) {
|
268 |
|
269 | if (column === false || column === true) {
|
270 | return this.where(1, '=', column ? 1 : 0);
|
271 | }
|
272 |
|
273 |
|
274 |
|
275 | if (typeof column === 'function') {
|
276 | return this.whereWrapped(column);
|
277 | }
|
278 |
|
279 |
|
280 | if (column instanceof Raw && arguments.length === 1)
|
281 | return this.whereRaw(column);
|
282 |
|
283 |
|
284 | if (isObject(column) && !(column instanceof Raw))
|
285 | return this._objectWhere(column);
|
286 |
|
287 |
|
288 |
|
289 |
|
290 | if (arguments.length === 2) {
|
291 | value = operator;
|
292 | operator = '=';
|
293 |
|
294 |
|
295 |
|
296 | if (value === null) {
|
297 | return this.whereNull(column);
|
298 | }
|
299 | }
|
300 |
|
301 |
|
302 | const checkOperator = `${operator}`.toLowerCase().trim();
|
303 |
|
304 |
|
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 |
|
321 |
|
322 | if (value === null) {
|
323 |
|
324 | if (checkOperator === 'is' || checkOperator === 'is not') {
|
325 | return this._not(checkOperator === 'is not').whereNull(column);
|
326 | }
|
327 | }
|
328 |
|
329 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
447 | orWhereExists(callback) {
|
448 | return this._bool('or').whereExists(callback);
|
449 | },
|
450 |
|
451 |
|
452 | whereNotExists(callback) {
|
453 | return this._not(true).whereExists(callback);
|
454 | },
|
455 |
|
456 |
|
457 | orWhereNotExists(callback) {
|
458 | return this._bool('or').whereNotExists(callback);
|
459 | },
|
460 |
|
461 |
|
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 |
|
477 | orWhereIn(column, values) {
|
478 | return this._bool('or').whereIn(column, values);
|
479 | },
|
480 |
|
481 |
|
482 | whereNotIn(column, values) {
|
483 | return this._not(true).whereIn(column, values);
|
484 | },
|
485 |
|
486 |
|
487 | orWhereNotIn(column, values) {
|
488 | return this._bool('or')
|
489 | ._not(true)
|
490 | .whereIn(column, values);
|
491 | },
|
492 |
|
493 |
|
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 |
|
506 | orWhereNull(column) {
|
507 | return this._bool('or').whereNull(column);
|
508 | },
|
509 |
|
510 |
|
511 | whereNotNull(column) {
|
512 | return this._not(true).whereNull(column);
|
513 | },
|
514 |
|
515 |
|
516 | orWhereNotNull(column) {
|
517 | return this._bool('or').whereNotNull(column);
|
518 | },
|
519 |
|
520 |
|
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 |
|
542 | whereNotBetween(column, values) {
|
543 | return this._not(true).whereBetween(column, values);
|
544 | },
|
545 |
|
546 |
|
547 | orWhereBetween(column, values) {
|
548 | return this._bool('or').whereBetween(column, values);
|
549 | },
|
550 |
|
551 |
|
552 | orWhereNotBetween(column, values) {
|
553 | return this._bool('or').whereNotBetween(column, values);
|
554 | },
|
555 |
|
556 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
655 | union(...args) {
|
656 | return this._union('union', args);
|
657 | },
|
658 |
|
659 |
|
660 | unionAll(...args) {
|
661 | return this._union('union all', args);
|
662 | },
|
663 |
|
664 |
|
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 |
|
691 | having(column, operator, value) {
|
692 | if (column instanceof Raw && arguments.length === 1) {
|
693 | return this.havingRaw(column);
|
694 | }
|
695 |
|
696 |
|
697 |
|
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 |
|
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 |
|
838 | orHavingIn(column, values) {
|
839 | return this._bool('or').havingIn(column, values);
|
840 | },
|
841 |
|
842 |
|
843 | havingNotIn(column, values) {
|
844 | return this._not(true).havingIn(column, values);
|
845 | },
|
846 |
|
847 |
|
848 | orHavingNotIn(column, values) {
|
849 | return this._bool('or')
|
850 | ._not(true)
|
851 | .havingIn(column, values);
|
852 | },
|
853 |
|
854 |
|
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 |
|
872 | offset(value) {
|
873 | if (isNil(value) || value instanceof Raw || value instanceof Builder) {
|
874 |
|
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 |
|
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 |
|
899 | count(column, options) {
|
900 | return this._aggregate('count', column || '*', options);
|
901 | },
|
902 |
|
903 |
|
904 | min(column, options) {
|
905 | return this._aggregate('min', column, options);
|
906 | },
|
907 |
|
908 |
|
909 | max(column, options) {
|
910 | return this._aggregate('max', column, options);
|
911 | },
|
912 |
|
913 |
|
914 | sum(column, options) {
|
915 | return this._aggregate('sum', column, options);
|
916 | },
|
917 |
|
918 |
|
919 | avg(column, options) {
|
920 | return this._aggregate('avg', column, options);
|
921 | },
|
922 |
|
923 |
|
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 |
|
941 | sumDistinct(column, options) {
|
942 | return this._aggregate('sum', column, { ...options, distinct: true });
|
943 | },
|
944 |
|
945 |
|
946 | avgDistinct(column, options) {
|
947 | return this._aggregate('avg', column, { ...options, distinct: true });
|
948 | },
|
949 |
|
950 |
|
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 |
|
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 |
|
977 | clearCounters() {
|
978 | this._single.counter = {};
|
979 |
|
980 | return this;
|
981 | },
|
982 |
|
983 |
|
984 |
|
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 |
|
1001 |
|
1002 | connection(_connection) {
|
1003 | this._connection = _connection;
|
1004 | return this;
|
1005 | },
|
1006 |
|
1007 |
|
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 |
|
1020 | clearSelect() {
|
1021 | this._clearGrouping('columns');
|
1022 | return this;
|
1023 | },
|
1024 |
|
1025 |
|
1026 | clearWhere() {
|
1027 | this._clearGrouping('where');
|
1028 | return this;
|
1029 | },
|
1030 |
|
1031 |
|
1032 | clearOrder() {
|
1033 | this._clearGrouping('order');
|
1034 | return this;
|
1035 | },
|
1036 |
|
1037 |
|
1038 | clearHaving() {
|
1039 | this._clearGrouping('having');
|
1040 | return this;
|
1041 | },
|
1042 |
|
1043 |
|
1044 |
|
1045 |
|
1046 |
|
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 |
|
1055 |
|
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 |
|
1082 | returning(returning) {
|
1083 | this._single.returning = returning;
|
1084 | return this;
|
1085 | },
|
1086 |
|
1087 |
|
1088 |
|
1089 |
|
1090 |
|
1091 | delete(ret) {
|
1092 | this._method = 'del';
|
1093 | if (!isEmpty(ret)) this.returning(ret);
|
1094 | return this;
|
1095 | },
|
1096 |
|
1097 |
|
1098 | truncate(tableName) {
|
1099 | this._method = 'truncate';
|
1100 | if (tableName) {
|
1101 | this._single.table = tableName;
|
1102 | }
|
1103 | return this;
|
1104 | },
|
1105 |
|
1106 |
|
1107 | columnInfo(column) {
|
1108 | this._method = 'columnInfo';
|
1109 | this._single.columnInfo = column;
|
1110 | return this;
|
1111 | },
|
1112 |
|
1113 |
|
1114 | forUpdate() {
|
1115 | this._single.lock = lockMode.forUpdate;
|
1116 | this._single.lockTables = helpers.normalizeArr.apply(null, arguments);
|
1117 | return this;
|
1118 | },
|
1119 |
|
1120 |
|
1121 | forShare() {
|
1122 | this._single.lock = lockMode.forShare;
|
1123 | this._single.lockTables = helpers.normalizeArr.apply(null, arguments);
|
1124 | return this;
|
1125 | },
|
1126 |
|
1127 |
|
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 |
|
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 |
|
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 |
|
1177 |
|
1178 | modify(callback) {
|
1179 | callback.apply(this, [this].concat(tail(arguments)));
|
1180 | return this;
|
1181 | },
|
1182 |
|
1183 |
|
1184 |
|
1185 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
1245 | _clearGrouping(grouping) {
|
1246 | this._statements = reject(this._statements, { grouping });
|
1247 | },
|
1248 |
|
1249 |
|
1250 | _isSelectQuery() {
|
1251 | return includes(['pluck', 'first', 'select'], this._method);
|
1252 | },
|
1253 |
|
1254 |
|
1255 | _hasLockMode() {
|
1256 | return includes([lockMode.forShare, lockMode.forUpdate], this._single.lock);
|
1257 | },
|
1258 | });
|
1259 |
|
1260 | Object.defineProperty(Builder.prototype, 'or', {
|
1261 | get() {
|
1262 | return this._bool('or');
|
1263 | },
|
1264 | });
|
1265 |
|
1266 | Object.defineProperty(Builder.prototype, 'not', {
|
1267 | get() {
|
1268 | return this._not(true);
|
1269 | },
|
1270 | });
|
1271 |
|
1272 | Builder.prototype.select = Builder.prototype.columns;
|
1273 | Builder.prototype.column = Builder.prototype.columns;
|
1274 | Builder.prototype.andWhereNot = Builder.prototype.whereNot;
|
1275 | Builder.prototype.andWhereNotColumn = Builder.prototype.whereNotColumn;
|
1276 | Builder.prototype.andWhere = Builder.prototype.where;
|
1277 | Builder.prototype.andWhereColumn = Builder.prototype.whereColumn;
|
1278 | Builder.prototype.andWhereRaw = Builder.prototype.whereRaw;
|
1279 | Builder.prototype.andWhereBetween = Builder.prototype.whereBetween;
|
1280 | Builder.prototype.andWhereNotBetween = Builder.prototype.whereNotBetween;
|
1281 | Builder.prototype.andHaving = Builder.prototype.having;
|
1282 | Builder.prototype.andHavingIn = Builder.prototype.havingIn;
|
1283 | Builder.prototype.andHavingNotIn = Builder.prototype.havingNotIn;
|
1284 | Builder.prototype.andHavingNull = Builder.prototype.havingNull;
|
1285 | Builder.prototype.andHavingNotNull = Builder.prototype.havingNotNull;
|
1286 | Builder.prototype.andHavingExists = Builder.prototype.havingExists;
|
1287 | Builder.prototype.andHavingNotExists = Builder.prototype.havingNotExists;
|
1288 | Builder.prototype.andHavingBetween = Builder.prototype.havingBetween;
|
1289 | Builder.prototype.andHavingNotBetween = Builder.prototype.havingNotBetween;
|
1290 | Builder.prototype.from = Builder.prototype.table;
|
1291 | Builder.prototype.into = Builder.prototype.table;
|
1292 | Builder.prototype.del = Builder.prototype.delete;
|
1293 |
|
1294 |
|
1295 | require('../interface')(Builder);
|
1296 | helpers.addQueryContext(Builder);
|
1297 |
|
1298 | Builder.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 |
|
1308 | module.exports = Builder;
|