Grammar   F
last analyzed

Complexity

Total Complexity 96

Size/Duplication

Total Lines 989
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 177
c 2
b 0
f 0
dl 0
loc 989
rs 2
wmc 96

60 Methods

Rating   Name   Duplication   Size   Complexity  
A compileBasicHaving() 0 7 1
A whereColumn() 0 3 1
A compileJoinContraint() 0 6 2
A compileInsertGetId() 0 3 1
A compileInsertUsing() 0 3 1
A whereExists() 0 3 1
A compileGroups() 0 3 1
A compileAggregate() 0 9 3
A compileExists() 0 5 1
A whereNested() 0 5 2
A compileUpdateWithJoins() 0 5 1
A compileLock() 0 3 2
A whereTime() 0 3 1
A removeStatementBoolean() 0 3 1
A whereDate() 0 3 1
A compileOffset() 0 3 1
A compileUnion() 0 5 2
A whereInRaw() 0 7 2
A concatenateWheresClauses() 0 5 2
A whereBasic() 0 5 1
A dateBasedWhere() 0 5 1
A whereNotInRaw() 0 7 2
A compileUpdate() 0 12 2
A whereMonth() 0 3 1
A compileColumns() 0 9 3
A compileDelete() 0 10 2
A compileOrders() 0 7 2
A compileUpdateWithoutJoins() 0 3 1
A compileHavings() 0 5 1
A whereNotInSub() 0 5 1
A compileLimit() 0 3 1
A whereYear() 0 3 1
A concatenate() 0 4 1
A compileJoins() 0 25 4
A compileWheresToArray() 0 9 2
A whereNotNull() 0 3 1
A compileHavingBetween() 0 10 2
A compileComponents() 0 13 3
A compileUnions() 0 21 5
A whereRaw() 0 3 1
A compileWheres() 0 11 3
A compileDeleteWithJoins() 0 7 1
A compileFrom() 0 3 1
A getUpdateColumns() 0 9 2
A whereDay() 0 3 1
A compileHaving() 0 9 3
A whereIn() 0 9 2
A whereNull() 0 3 1
A whereBetween() 0 5 2
A compileSelect() 0 7 2
A compileDeleteWithoutJoins() 0 3 1
A compileTruncate() 0 3 1
A whereNotExists() 0 3 1
A compileOrderToArray() 0 5 1
A whereSub() 0 5 1
A compileRandom() 0 3 1
A whereInSub() 0 5 1
A compileInsert() 0 21 3
A whereNotIn() 0 9 2
A wrapUnion() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Grammar often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Grammar, and based on these observations, apply Extract Interface, too.

1
<?php 
2
3
/**
4
 * Lenevor PHP Framework
5
 *
6
 * LICENSE
7
 *
8
 * This source file is subject to the new BSD license that is bundled
9
 * with this package in the file license.md.
10
 * It is also available through the world-wide-web at this URL:
11
 * https://lenevor.com/license
12
 * If you did not receive a copy of the license and are unable to
13
 * obtain it through the world-wide-web, please send an email
14
 * to [email protected] so we can send you a copy immediately.
15
 *
16
 * @package     Lenevor
17
 * @subpackage  Base
18
 * @link        https://lenevor.com
19
 * @copyright   Copyright (c) 2019 - 2021 Alexander Campo <[email protected]>
20
 * @license     https://opensource.org/licenses/BSD-3-Clause New BSD license or see https://lenevor.com/license or see /license.md
21
 */
22
 
23
namespace Syscodes\Database\Query;
24
25
use Syscodes\Database\Grammar as BaseGrammar;
26
27
/**
28
 * Allows make the grammar's for get results of the database.
29
 * 
30
 * @author Alexander Campo <[email protected]>
31
 */
32
class Grammar extends BaseGrammar
33
{
34
    /**
35
     * Get the components for use a select statement.
36
     * 
37
     * @var array $components
38
     */
39
    protected $components = [
40
        'aggregate',
41
        'columns',
42
        'from',
43
        'joins',
44
        'wheres',
45
        'groups',        
46
        'havings',
47
        'orders',
48
        'limit',
49
        'offset',
50
        'unions'
51
    ];
52
53
    /**
54
     * Compile a select query into sql.
55
     * 
56
     * @param  \Syscodes\Database\Query\Builder  $builder
57
     * 
58
     * @return string
59
     */
60
    public function compileSelect(Builder $builder)
61
    {
62
        if (is_null($builder->columns)) {
0 ignored issues
show
introduced by
The condition is_null($builder->columns) is always false.
Loading history...
63
            $builder->columns = ['*'];
64
        }
65
66
        return trim($this->concatenate($this->compileComponents($builder)));
67
    }
68
69
    /**
70
     * Compile the components necessary for a select clause.
71
     * 
72
     * @param  \Syscodes\Database\Query\Builder  $builder
73
     * 
74
     * @return array
75
     */
76
    protected function compileComponents(Builder $builder)
77
    {
78
        $sql = [];
79
80
        foreach ($this->components as $component) {
81
            if ( ! is_null($builder->$component)) {
82
                $method = 'compile'.ucfirst($component);
83
84
                $sql[$component] = $this->$method($builder, $builder->$component);
85
            }
86
        }
87
88
        return $sql;
89
    }
90
91
    /**
92
     * Compile an aggregated select clause.
93
     * 
94
     * @param  \Syscodes\Database\Query\Builder  $builder
95
     * @param  array  $aggregate
96
     * 
97
     * @return string
98
     */
99
    protected function compileAggregate(Builder $builder, $aggregate)
100
    {
101
        $column = $this->columnize($aggregate['columns']);
102
103
        if ($builder->distinct && $column !== '*') {
104
            $column = 'distinct '.$column;
105
        }
106
107
        return 'select '.$aggregate['function'].'('.$column.') as aggregate';
108
    }
109
110
    /**
111
     * Compile the "select *" portion of the query.
112
     * 
113
     * @param  \Syscodes\Database\Query\Builder  $builder
114
     * @param  array  $columns
115
     * 
116
     * @return string
117
     */
118
    protected function compileColumns(Builder $builder, $columns)
119
    {
120
        if (is_null($builder->columns)) {
0 ignored issues
show
introduced by
The condition is_null($builder->columns) is always false.
Loading history...
121
            return;
122
        }
123
124
        $select = $builder->distinct ? 'select distinct ' : 'select ';
125
126
        return $select.$this->columnize($columns);
127
    }
128
129
    /**
130
     * Compile the "from" portion of the query.
131
     * 
132
     * @param  \Syscodes\Database\Query\Builder  $builder
133
     * @param  string  $table
134
     * 
135
     * @return string
136
     */
137
    protected function compileFrom(Builder $builder, $table)
138
    {
139
        return 'from '.$this->wrapTable($table);
140
    }
141
142
    /**
143
     * Compile the "join" portions of the query.
144
     * 
145
     * @param  \Syscodes\Database\Query\Builder  $builder
146
     * @param  array  $joins
147
     * 
148
     * @return string
149
     */
150
    protected function compileJoins(Builder $builder, $joins)
151
    {
152
        $sql = [];
153
154
        foreach ((array) $joins as $join) {
155
            $table = $this->wrapTable($join->table);
156
157
            $clauses = [];
158
159
            foreach ($join->clauses as $clause)  {
160
                $clauses[] = $this->compileJoinContraint($clause);
161
            }
162
163
            foreach ($join->bindings as $binding) {
164
                $query->addBinding($binding, 'join');
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $query seems to be never defined.
Loading history...
165
            }
166
167
            $clauses[0] = $this->removeStatementBoolean($clauses[0]);
168
169
            $clauses = implode(' ', $clauses);
170
171
            $sql[] = "{$join->type} join {$table} on {$clauses}";
172
        }
173
174
        return implode(' ', $sql);
175
    }
176
177
    /**
178
     * Create a join clause constraint segment.
179
     * 
180
     * @param  array  $clause
181
     * 
182
     * @return string
183
     */
184
    protected function compileJoinContraint(array $clause)
185
    {
186
        $first  = $this->wrap($clause['first']);
187
        $second = $clause['where'] ? '?' : $this->wrap($clause['second']);
188
189
        return "{$clause['boolean']} $first {$clause['operator']} $second";
190
    }
191
192
    /**
193
     * Compile the "where" portions of the query.
194
     * 
195
     * @param  \Syscodes\Database\Query\Builder  $builder
196
     * 
197
     * @return string
198
     */
199
    protected function compileWheres(Builder $builder)
200
    {
201
       if (is_null($builder->wheres)) {
0 ignored issues
show
introduced by
The condition is_null($builder->wheres) is always false.
Loading history...
202
           return '';
203
       }
204
205
       if (count($sql = $this->compileWheresToArray($builder)) > 0) {
206
            return $this->concatenateWheresClauses($builder, $sql);
207
       }
208
209
       return '';
210
    }
211
212
    /**
213
     * Get an array of all the where clauses for the query.
214
     * 
215
     * @param  \Syscodes\Database\Query\Builder  $query
216
     * 
217
     * @return array
218
     */
219
    protected function compileWheresToArray($query)
220
    {
221
        $sql = [];
222
223
        foreach ($query->wheres as $where) {
224
            $sql[] = $where['boolean'].' '.$this->{"where{$where['type']}"}($query, $where);
225
        }
226
227
        return $sql;
228
    }
229
230
    /**
231
     * Format the where clause statements into one string.
232
     * 
233
     * @param  \Syscodes\Database\Query\Builder  $builder
234
     * @param  array  $sql
235
     * 
236
     * @return string
237
     */
238
    protected function concatenateWheresClauses($builder, $sql)
239
    {
240
        $statement = $builder->joins ? 'on' : 'where';
241
242
        return $statement.' '.$this->removeStatementBoolean(implode(' ', $sql));
243
    }
244
245
    /**
246
     * Compile a raw where clause.
247
     * 
248
     * @param  \Syscodes\Database\Query\Builder  $builder
249
     * @param  array  $where
250
     * 
251
     * @return string
252
     */
253
    protected function whereRaw(Builder $builder, $where)
254
    {
255
        return $where['sql'];
256
    }
257
258
    /**
259
     * Compile a basic where clause.
260
     * 
261
     * @param  \Syscodes\Database\Query\Builder  $builder
262
     * @param  array  $where
263
     * 
264
     * @return string
265
     */
266
    protected function whereBasic(Builder $builder, $where)
267
    {
268
        $operator = str_replace('?', '??', $where['operator']);
0 ignored issues
show
Unused Code introduced by
The assignment to $operator is dead and can be removed.
Loading history...
269
       
270
        return $this->wrap($where['column']).' '.$where['operator'].' '.$where['value'];
271
    }
272
273
    /**
274
     * Compile a "between" where clause.
275
     * 
276
     * @param  \Syscodes\Database\Query\Builder  $builder
277
     * @param  array  $where
278
     * 
279
     * @return string
280
     */
281
    protected function whereBetween(Builder $builder, $where)
282
    {
283
        $between = $where['not'] ? 'not between' : 'between';
284
285
        return $this->wrap($where['column']).' '.$between.' ? and ?';
286
    }
287
288
    /**
289
     * Compile a where exists clause.
290
     * 
291
     * @param  \Syscodes\Database\Query\Builder  $builder
292
     * @param  array  $where
293
     * 
294
     * @return string
295
     */
296
    protected function whereExists(Builder $builder, $where)
297
    {
298
        return 'exists ('.$this->compileSelect($where['query']).')';
299
    }
300
301
    /**
302
     * Compile a where exists clause.
303
     * 
304
     * @param  \Syscodes\Database\Query\Builder  $builder
305
     * @param  array  $where
306
     * 
307
     * @return string
308
     */
309
    protected function whereNotExists(Builder $builder, $where)
310
    {
311
        return 'not exists ('.$this->compileSelect($where['query']).')';
312
    }
313
314
    /**
315
     * Compile a "where in" clause.
316
     * 
317
     * @param  \Syscodes\Database\Query\Builder  $builder
318
     * @param  array  $where
319
     * 
320
     * @return string
321
     */
322
    protected function whereIn(Builder $builder, $where)
323
    {
324
        $values = $this->parameterize($where['query']);
325
326
        if ( ! empty($where['query'])) {
327
            return $this->wrap($where['column']).' in ('.$values.')';
328
        }
329
330
        return '0 = 1';
331
    }
332
333
    /**
334
     * Compile a "where not in" clause.
335
     * 
336
     * @param  \Syscodes\Database\Query\Builder  $builder
337
     * @param  array  $where
338
     * 
339
     * @return string
340
     */
341
    protected function whereNotIn(Builder $builder, $where)
342
    {
343
        $values = $this->parameterize($where['query']);
344
345
        if ( ! empty($where['query'])) {
346
            return $this->wrap($where['column']).' not in ('.$values.')';
347
        }
348
349
        return '1 = 1';
350
    }
351
352
    /**
353
     * Compile a "where not in raw" clause.
354
     * 
355
     * @param  \Syscodes\Database\Query\Builder  $builder
356
     * @param  array  $where
357
     * 
358
     * @return string
359
     */
360
    protected function whereNotInRaw(Builder $builder, $where)
361
    {
362
        if ( ! empty($where['query'])) {
363
            return $this->wrap($where['column']).' not in ('.implode(', ', $where['values']).')';
364
        }
365
366
        return '1 = 1';
367
    }
368
369
    /**
370
     * Compile a "where in raw" clause.
371
     * 
372
     * @param  \Syscodes\Database\Query\Builder  $builder
373
     * @param  array  $where
374
     * 
375
     * @return string
376
     */
377
    protected function whereInRaw(Builder $builder, $where)
378
    {
379
        if ( ! empty($where['query'])) {
380
            return $this->wrap($where['column']).' in ('.implode(', ', $where['values']).')';
381
        }
382
383
        return '0 = 1';
384
    }
385
386
    /**
387
     * Compile a where in sub-select clause.
388
     * 
389
     * @param  \Syscodes\Database\Query\Builder  $builder
390
     * @param  array  $where
391
     * 
392
     * @return string
393
     */
394
    protected function whereInSub(Builder $builder, $where)
395
    {
396
        $select = $this->compileSelect($where['query']);
397
398
        return $this->wrap($where['column']).' in ('.$select.')';
399
    }
400
401
    /**
402
     * Compile a where not in sub-select clause.
403
     * 
404
     * @param  \Syscodes\Database\Query\Builder  $builder
405
     * @param  array  $where
406
     * 
407
     * @return string
408
     */
409
    protected function whereNotInSub(Builder $builder, $where)
410
    {
411
        $select = $this->compileSelect($where['query']);
412
413
        return $this->wrap($where['column']).' not in ('.$select.')';
414
    }
415
416
    /**
417
     * Compile a "where null" clause.
418
     * 
419
     * @param  \Syscodes\Database\Query\Builder  $builder
420
     * @param  array  $where
421
     * 
422
     * @return string
423
     */
424
    protected function whereNull(Builder $builder, $where)
425
    {
426
        return $this->wrap($where['column']).' is null';
427
    }
428
429
    /**
430
     * Compile a "where not null" clause.
431
     * 
432
     * @param  \Syscodes\Database\Query\Builder  $builder
433
     * @param  array  $where
434
     * 
435
     * @return string
436
     */
437
    protected function whereNotNull(Builder $builder, $where)
438
    {
439
        return $this->wrap($where['column']).' not is null';
440
    }
441
442
    /**
443
     * Compile a "where date" clause.
444
     * 
445
     * @param  \Syscodes\Database\Query\Builder  $builder
446
     * @param  array  $where
447
     * 
448
     * @return string
449
     */
450
    protected function whereDate(Builder $builder, $where)
451
    {
452
        return $this->dateBasedWhere('date', $builder, $where);
453
    }
454
455
    /**
456
     * Compile a "where time" clause.
457
     * 
458
     * @param  \Syscodes\Database\Query\Builder  $builder
459
     * @param  array  $where
460
     * 
461
     * @return string
462
     */
463
    protected function whereTime(Builder $builder, $where)
464
    {
465
        return $this->dateBasedWhere('time', $builder, $where);
466
    }
467
468
    /**
469
     * Compile a "where day" clause.
470
     * 
471
     * @param  \Syscodes\Database\Query\Builder  $builder
472
     * @param  array  $where
473
     * 
474
     * @return string
475
     */
476
    protected function whereDay(Builder $builder, $where)
477
    {
478
        return $this->dateBasedWhere('day', $builder, $where);
479
    }
480
481
    /**
482
     * Compile a "where month" clause.
483
     * 
484
     * @param  \Syscodes\Database\Query\Builder  $builder
485
     * @param  array  $where
486
     * 
487
     * @return string
488
     */
489
    protected function whereMonth(Builder $builder, $where)
490
    {
491
        return $this->dateBasedWhere('month', $builder, $where);
492
    }
493
494
    /**
495
     * Compile a "where year" clause.
496
     * 
497
     * @param  \Syscodes\Database\Query\Builder  $builder
498
     * @param  array  $where
499
     * 
500
     * @return string
501
     */
502
    protected function whereYear(Builder $builder, $where)
503
    {
504
        return $this->dateBasedWhere('year', $builder, $where);
505
    }
506
507
    /**
508
     * Compile a date based where clause.
509
     * 
510
     * @param  string  $type
511
     * @param  \Syscodes\Database\Query\Builder  $builder
512
     * @param  array  $where
513
     * 
514
     * @return string
515
     */
516
    protected function dateBasedWhere($type, Builder $builder, $where)
517
    {
518
        $value = $this->parameter($where['value']);
519
520
        return $type.'('.$this->wrap($where['column']).') '.$where['operator'].' '.$value;
521
    }
522
523
    /**
524
     * Compile a nested where clause.
525
     * 
526
     * @param  \Syscodes\Database\Query\Builder  $builder
527
     * @param  array  $where
528
     * 
529
     * @return string
530
     */
531
    protected function whereNested(Builder $builder, $where)
532
    {
533
        $intClause = $builder instanceof JoinClause ? 3 : 6;
0 ignored issues
show
introduced by
$builder is never a sub-type of Syscodes\Database\Query\JoinClause.
Loading history...
534
535
        return '('.substr($this->compileWheres($where['query']), $intClause).')';
536
    }
537
538
    /**
539
     * Compile a where condition with a sub-select.
540
     * 
541
     * @param  \Syscodes\Database\Query\Builder  $builder
542
     * @param  array  $where
543
     * 
544
     * @return string
545
     */
546
    protected function whereSub(Builder $builder, $where)
547
    {
548
        $select = $this->compileSelect($where['query']);
549
550
        return $this->wrap($where['column']).' '.$where['operator']." ($select)";
551
    }
552
553
    /**
554
     * Compile a where clause comparing two columns.
555
     * 
556
     * @param  \Syscodes\Database\Query\Builder  $builder
557
     * @param  array  $where
558
     * 
559
     * @return string
560
     */
561
    protected function whereColumn(Builder $builder, $where)
562
    {
563
        return $this->wrap($where['first']).' '.$where['operator'].' '.$this->wrap($where['second']);
564
    }
565
566
    /**
567
     * Compile the "group by" portions of the query.
568
     * 
569
     * @param  \Syscodes\Database\Query\Builder  $builder
570
     * @param  array  $groups
571
     * 
572
     * @return string
573
     */
574
    protected function compileGroups(Builder $builder, $groups)
575
    {
576
        return 'group by '.$this->columnize($groups);
577
    }
578
579
    /**
580
     * Compile the "having" portions of the query.
581
     * 
582
     * @param  \Syscodes\Database\Query\Builder  $builder
583
     * @param  array  $havings
584
     * 
585
     * @return string
586
     */
587
    protected function compileHavings(Builder $builder, $havings)
588
    {
589
        $sql = implode(' ', array_map([$this, 'compileHaving'], $havings));
590
591
        return 'having '.$this->removeStatementBoolean($sql);
592
    }
593
594
    /**
595
     * Compile a single having clause.
596
     * 
597
     * @param  array  $having
598
     * 
599
     * @return string
600
     */
601
    protected function compileHaving(array $having)
602
    {
603
        if ($having['type'] === 'raw') {
604
            return $having['boolean'].' '.$having['sql'];
605
        } elseif ($having['type'] === 'between') {
606
            return $this->compileHavingBetween($having);
607
        }
608
609
        return $this->compileBasicHaving($having);
610
    }
611
612
    /**
613
     * Compile a "between" having clause.
614
     * 
615
     * @param  array  $having
616
     * 
617
     * @return string
618
     */
619
    protected function compileHavingBetween($having)
620
    {
621
        $between = $having['not'] ? 'not between' : 'between';
622
623
        $column = $this->wrap($having['column']);
624
625
        $min = $this->parameter(head($having['values']));
626
        $max = $this->parameter(last($having['values']));
627
628
        return $having['boolean'].' '.$column.' '.$between.' '.$min.' and '.$max;
629
    }
630
631
    /**
632
     * Compile a "basic" having clause.
633
     * 
634
     * @param  array  $having
635
     * 
636
     * @return string
637
     */
638
    protected function compileBasicHaving($having)
639
    {
640
        $column = $this->wrap($having['column']);
641
642
        $parameter = $this->parameter($having['value']);
643
644
        return $having['boolean'].' '.$column.' '.$having['operator'].' '.$parameter;
645
    }
646
647
    /**
648
     * Compile the "order by" portions of the query.
649
     * 
650
     * @param  \Syscodes\Database\Query\Builder  $builder
651
     * @param  array  $orders
652
     * 
653
     * @return string
654
     */
655
    protected function compileOrders(Builder $builder, $orders)
656
    {
657
        if ( ! empty($orders)) {
658
            return 'order by '.implode(', ', $this->compileOrderToArray($builder, $orders));
0 ignored issues
show
Bug introduced by
$this->compileOrderToArray($builder, $orders) of type string is incompatible with the type array expected by parameter $pieces of implode(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

658
            return 'order by '.implode(', ', /** @scrutinizer ignore-type */ $this->compileOrderToArray($builder, $orders));
Loading history...
659
        }
660
661
        return '';
662
    }
663
664
    /**
665
     * Compile the query orders to an array.
666
     * 
667
     * @param  \Syscodes\Database\Query\Builder  $builder
668
     * @param  array  $orders
669
     * 
670
     * @return string
671
     */
672
    protected function compileOrderToArray(Builder $builder, $orders)
673
    {
674
        return array_map(function ($order) {
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_map(functio...{ /* ... */ }, $orders) returns the type array which is incompatible with the documented return type string.
Loading history...
675
            return $order['sql'] ?? $this->wrap($order['column']).' '.$order['direction'];
676
        }, $orders);
677
    }
678
679
    /**
680
     * Compile the "limit" portions of the query.
681
     * 
682
     * @param  \Syscodes\Database\Query\Builder  $builder
683
     * @param  int  $limit
684
     * 
685
     * @return string
686
     */
687
    protected function compileLimit(Builder $builder, $limit)
688
    {
689
        return 'limit '.(int) $limit;
690
    }
691
692
    /**
693
     * Compile the "offset" portions of the query.
694
     * 
695
     * @param  \Syscodes\Database\Query\Builder  $builder
696
     * @param  int  $offset
697
     * 
698
     * @return string
699
     */
700
    protected function compileOffset(Builder $builder, $offset)
701
    {
702
        return 'offset '.(int) $offset;
703
    }
704
705
    /**
706
     * Compile the "union" queries attached to the main query.
707
     * 
708
     * @param  \Syscodes\Database\Query\Builder  $builder
709
     * 
710
     * @return string
711
     */
712
    protected function compileUnions(Builder $builder)
713
    {
714
        $sql = '';
715
716
        foreach ($builder->unions as $union) {
717
            $sql .= $this->compileUnion($union);
718
        }
719
720
        if ( ! empty($builder->unionOrders)) {
721
            $sql .= ' '.$this->compileOrders($builder, $builder->unionOrders);
722
        }
723
724
        if (isset($builder->unionLimit)) {
725
            $sql .= ' '.$this->compileLimit($builder, $builder->unionLimit);
726
        }
727
728
        if (isset($builder->unionOffset)) {
729
            $sql .= ' '.$this->compileOffset($builder, $builder->unionOffset);
730
        }
731
732
        return ltrim($sql);
733
    }
734
735
    /**
736
     * Compile a single union statement.
737
     * 
738
     * @param  array  $union
739
     * 
740
     * @return string
741
     */
742
    protected function compileUnion(array $union)
743
    {
744
        $joiner = $union['all'] ? ' union all ' : ' union ';
745
746
        return $joiner.$this->wrapUnion($union['query']->tosql());
747
    }
748
749
    /**
750
     * Wrap a union subquery in parentheses.
751
     * 
752
     * @param  string  $sql
753
     * 
754
     * @return string
755
     */
756
    protected function wrapUnion($sql)
757
    {
758
        return '('.$sql.')';
759
    }
760
761
    /**
762
     * Compile the random statement into SQL.
763
     * 
764
     * @param  string  $seed
765
     * 
766
     * @return string
767
     */
768
    public function compileRandom($seed)
769
    {
770
        return 'RANDOM()';
771
    }
772
773
    /**
774
     * Compile an exists statement into SQL.
775
     * 
776
     * @param  \Syscodes\Database\Query\Builder  $builder
777
     * 
778
     * @return string
779
     */
780
    public function compileExists(Builder $builder)
781
    {
782
        $select = $this->compileSelect($builder);
783
784
        return "select exists({$select}) as {$this->wrap('exists')}";
785
    }
786
787
    /**
788
     * Compile an insert statement into SQL.
789
     * 
790
     * @param  \Syscodes\Database\Query\Builder  $builder
791
     * @param  array  $values
792
     * 
793
     * @return string
794
     */
795
    public function compileInsert(Builder $builder, array $values)
796
    {
797
        $table = $this->wrapTable($builder->from);
798
799
        if (empty($values)) {
800
            return "insert into {$table} default values";
801
        }
802
803
        if ( ! is_array(head($values))) {
804
            $values = [$values];
805
        }
806
807
        $columns = $this->columnize(array_keys(head($values)));
808
809
        $parameters = $this->parameterize(head($values));
810
811
        $value = array_fill(0, count($values), "($parameters)");
812
813
        $parameters = implode(', ', $value);
814
815
        return "insert into $table ($columns) values $parameters";
816
    }
817
818
    /**
819
     * Compile an insert and get ID statement into SQL.
820
     * 
821
     * @param  \Syscodes\Database\Query\Builder  $builder
822
     * @param  array  $values
823
     * @param  string  $sequence
824
     * 
825
     * @return string
826
     */
827
    public function compileInsertGetId(Builder $builder, $values, $sequence)
828
    {
829
        return $this->compileInsert($builder, $values);
830
    }
831
832
    /**
833
     * Compile an insert statement using a subquery into SQL.
834
     * 
835
     * @param  \Syscodes\Database\Query\Builder  $builder
836
     * @param  array  $columns
837
     * @param  string  $sql
838
     * 
839
     * @return string
840
     */
841
    public function compileInsertUsing(Builder $builder, $columns, $sql)
842
    {
843
        return "insert into {$this->wrapTable($builder->from)} ({$this->columnize($columns)}) $sql";
844
    }
845
846
    /**
847
     * Compile an update statement into SQL.
848
     * 
849
     * @param  \Syscodes\Database\Query\Builder  $builder
850
     * @param  array  $values
851
     * 
852
     * @return string
853
     */
854
    public function compileUpdate(Builder $builder, array $values)
855
    {
856
        $table = $this->wrapTable($builder->from);
857
858
        $columns = $this->getUpdateColumns($values);
859
860
        $where = $this->compileWheres($builder);
861
862
        return trim(
863
            isset($builder->joins)
864
                ? $this->compileUpdateWithJoins($builder, $table, $columns, $where)
865
                : $this->compileUpdateWithoutJoins($builder, $table, $columns, $where)
866
        );
867
    }
868
869
    /**
870
     * Compile the columns for an update statement.
871
     * 
872
     * @param  array  $values
873
     * 
874
     * @return string
875
     */
876
    public function getUpdateColumns(array $values)
877
    {
878
        $columns = [];
879
880
        foreach ($values as $key => $value) {
881
            $columns[] = $this->wrap($key).' = '.$this->parameter($value);
882
        }
883
884
        return implode(', ', $columns);
885
    }
886
887
    /**
888
     * Compile an update statement with joins into SQL.
889
     * 
890
     * @param  \Syscodes\Database\Query\Builder  $builder
891
     * @param  string  $table
892
     * @param  string  $columns
893
     * @param  string  $where
894
     * 
895
     * @return string
896
     */
897
    public function compileUpdateWithJoins(Builder $builder, $table, $columns, $where)
898
    {
899
        $joins = $this->compileJoins($builder, $builder->joins);
900
901
        return "update {$table} {$joins} set {$columns} {$where}";
902
    }
903
904
    /**
905
     * Compile an update statement without joins into SQL.
906
     * 
907
     * @param  \Syscodes\Database\Query\Builder  $builder
908
     * @param  string  $table
909
     * @param  string  $columns
910
     * @param  string  $where
911
     * 
912
     * @return string
913
     */
914
    public function compileUpdateWithoutJoins(Builder $builder, $table, $columns, $where)
915
    {
916
       return "update {$table} set {$columns} {$where}";
917
    }
918
919
    /**
920
     * Compile a delete statement into SQL.
921
     * 
922
     * @param  \Syscodes\Database\Query\Builder  $builder
923
     * 
924
     * @return string
925
     */
926
    public function compileDelete(Builder $builder)
927
    {
928
        $table = $this->wrapTable($builder->from);
929
930
        $where = $this->compileWheres($builder);
931
932
        return trim(
933
            isset($builder->joins)
934
                ? $this->compileDeleteWithJoins($builder, $table, $where)
935
                : $this->compileDeleteWithoutJoins($builder, $table, $where)
936
        );
937
    }
938
939
    /**
940
     * Compile an delete statement with joins into SQL.
941
     * 
942
     * @param  \Syscodes\Database\Query\Builder  $builder
943
     * @param  string  $table
944
     * @param  string  $columns
945
     * @param  string  $where
946
     * 
947
     * @return string
948
     */
949
    public function compileDeleteWithJoins(Builder $builder, $table, $where)
950
    {
951
        $alias = last(explode(' as ', $table));
952
953
        $joins = $this->compileJoins($builder, $builder->joins);
954
955
        return "delete {$alias} from {$table} {$joins} {$where}";
956
    }
957
958
    /**
959
     * Compile an delete statement without joins into SQL.
960
     * 
961
     * @param  \Syscodes\Database\Query\Builder  $builder
962
     * @param  string  $table
963
     * @param  string  $where
964
     * 
965
     * @return string
966
     */
967
    public function compileDeleteWithoutJoins(Builder $builder, $table, $where)
968
    {
969
       return "delete from {$table} {$where}";
970
    }
971
972
    /**
973
     * Compile a truncate table statement into SQL.
974
     * 
975
     * @param  \Syscodes\Database\Query\Builder  $builder
976
     * 
977
     * @return array
978
     */
979
    public function compileTruncate(Builder $builder)
980
    {
981
        return ['truncate table '.$this->wrapTable($builder->from) => []];
982
    }
983
984
    /**
985
     * Compile the lock into SQL.
986
     * 
987
     * @param  \Syscodes\Database\Query\Builder  $builder
988
     * @param  bool|string  $value
989
     * 
990
     * @return string
991
     */
992
    public function compileLock(Builder $builder, $value)
993
    {
994
        return is_string($value) ? $value : '';
995
    }
996
997
    /**
998
     * Concatenate an array of segments, removing empties.
999
     * 
1000
     * @param  array  $segments
1001
     * 
1002
     * @return string
1003
     */
1004
    protected function concatenate($segments)
1005
    {
1006
        return implode(' ', array_filter($segments, function ($value) {
1007
            return (string) $value !== '';
1008
        }));
1009
    }
1010
1011
    /**
1012
     * Remove the leading boolean from a statement.
1013
     * 
1014
     * @param  string  $value
1015
     * 
1016
     * @return
1017
     */
1018
    protected function removeStatementBoolean($value)
1019
    {
1020
        return preg_replace('/and |or /', '', $value, 1);
1021
    }
1022
}