Passed
Push — main ( 5e0bcd...9ce40a )
by Peter
02:12
created

Select::addLock()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 14
c 0
b 0
f 0
nc 8
nop 1
dl 0
loc 20
rs 8.4444
1
<?php
2
3
declare(strict_types=1);
4
5
namespace QB\PostgreSQL\Statement;
6
7
use QB\Generic\Statement\Select as GenericSelect;
8
9
class Select extends GenericSelect
10
{
11
    protected const UNION     = 'union';
12
    protected const INTERSECT = 'intersect';
13
    protected const EXCEPT    = 'except';
14
15
    public const LOCK_FOR_UPDATE        = 'FOR UPDATE';
16
    public const LOCK_FOR_NO_KEY_UPDATE = 'FOR NO KEY UPDATE';
17
    public const LOCK_FOR_SHARE         = 'FOR SHARE';
18
    public const LOCK_FOR_KEY_SHARE     = 'FOR KEY SHARE';
19
    public const LOCK_NOWAIT            = 'NOWAIT';
20
    public const LOCK_SKIP_LOCKED       = 'SKIP LOCKED';
21
22
    protected array $unionLikes = [];
23
24
    /** @var string[] */
25
    protected array $locks = [];
26
27
    protected ?string $lockTable = null;
28
29
    protected ?int $outerOffset = null;
30
31
    protected ?int $outerLimit = null;
32
33
    /** @var array<string,string> */
34
    protected array $outerOrderByParts = [];
35
36
    /**
37
     * @param int|null $offset
38
     *
39
     * @return $this
40
     */
41
    public function setOuterOffset(?int $offset): static
42
    {
43
        $this->outerOffset = $offset;
44
45
        return $this;
46
    }
47
48
    /**
49
     * @param int|null $limit
50
     *
51
     * @return $this
52
     */
53
    public function setOuterLimit(?int $limit): static
54
    {
55
        $this->outerLimit = $limit;
56
57
        return $this;
58
    }
59
60
    /**
61
     * @param string $column
62
     * @param string $direction
63
     *
64
     * @return Select
65
     */
66
    public function addOuterOrderBy(string $column, string $direction = 'ASC'): static
67
    {
68
        $this->outerOrderByParts[$column] = $direction;
69
70
        return $this;
71
    }
72
73
    /**
74
     * @param string ...$locks
75
     *
76
     * @return $this
77
     */
78
    public function addLock(string ...$locks): static
79
    {
80
        foreach ($locks as $lock) {
81
            switch ($lock) {
82
                case static::LOCK_FOR_SHARE:
83
                case static::LOCK_FOR_UPDATE:
84
                case static::LOCK_FOR_KEY_SHARE:
85
                case static::LOCK_FOR_NO_KEY_UPDATE:
86
                    $this->locks[0] = $lock;
87
                    break;
88
                case static::LOCK_NOWAIT:
89
                case static::LOCK_SKIP_LOCKED:
90
                    $this->locks[1] = $lock;
91
                    break;
92
            }
93
        }
94
95
        ksort($this->locks);
96
97
        return $this;
98
    }
99
100
    /**
101
     * @param string $lockTable
102
     *
103
     * @return $this
104
     */
105
    public function addLockTable(string $lockTable): static
106
    {
107
        $this->lockTable = $lockTable;
108
109
        return $this;
110
    }
111
112
    /**
113
     * @param Select $select
114
     * @param string $modifier
115
     *
116
     * @return $this
117
     */
118
    public function addUnion(Select $select, string $modifier = ''): static
119
    {
120
        return $this->addUnionLike(static::UNION, $select, $modifier);
121
    }
122
123
    /**
124
     * @param Select $select
125
     * @param string $modifier
126
     *
127
     * @return $this
128
     */
129
    public function addIntersect(Select $select, string $modifier = ''): static
130
    {
131
        return $this->addUnionLike(static::INTERSECT, $select, $modifier);
132
    }
133
134
    /**
135
     * @param Select $select
136
     * @param string $modifier
137
     *
138
     * @return $this
139
     */
140
    public function addExcept(Select $select, string $modifier = ''): static
141
    {
142
        return $this->addUnionLike(static::EXCEPT, $select, $modifier);
143
    }
144
145
    /**
146
     * @param string $type
147
     * @param Select $select
148
     * @param string $modifier
149
     *
150
     * @return $this
151
     */
152
    protected function addUnionLike(string $type, Select $select, string $modifier = ''): static
153
    {
154
        if ($type !== static::INTERSECT && $type !== static::EXCEPT) {
155
            $type = static::UNION;
156
        }
157
158
        if ($modifier !== static::ALL && $modifier !== static::DISTINCT) {
159
            $modifier = '';
160
        }
161
162
        $this->unionLikes[] = [$type, $select, $modifier];
163
164
        return $this;
165
    }
166
167
    public function __toString(): string
168
    {
169
        $parts = array_merge(
170
            [parent::__toString()],
171
            $this->getLocks(),
172
            $this->getUnionLikes()
173
        );
174
175
        $parts = array_filter($parts);
176
177
        $sql = implode(PHP_EOL, $parts);
178
179
        if ($this->outerLimit === null && $this->outerOffset === null && count($this->outerOrderByParts) === 0) {
180
            return $sql;
181
        }
182
183
        $parts = array_merge(
184
            ['(' . $sql . ')'],
185
            $this->getOuterOrderBy(),
186
            $this->getOuterLimit()
187
        );
188
189
        $parts = array_filter($parts);
190
191
        return implode(PHP_EOL, $parts);
192
    }
193
194
    protected function getLocks(): array
195
    {
196
        $locks = [];
197
        $locks[] = array_key_exists(0, $this->locks) ? $this->locks[0] : '';
198
        $locks[] = $this->lockTable ? 'OF ' . $this->lockTable : '';
199
        $locks[] = array_key_exists(1, $this->locks) ? $this->locks[1] : '';
200
201
        $locks = array_filter($locks);
202
203
        return [implode(' ', $locks)];
204
    }
205
206
    public function getUnionLikes(): array
207
    {
208
        $parts = [];
209
        foreach ($this->unionLikes as $unionLike) {
210
            $unionType = $unionLike[0];
211
            $select    = $unionLike[1];
212
            $modifier  = '';
213
            if ($unionLike[2]) {
214
                $modifier = ' ' . $unionLike[2];
215
            }
216
217
            switch ($unionType) {
218
                case static::UNION:
219
                    $parts[] = 'UNION' . $modifier . PHP_EOL . $select;
220
                    break;
221
                case static::INTERSECT:
222
                    $parts[] = 'INTERSECT' . $modifier . PHP_EOL . $select;
223
                    break;
224
                case static::EXCEPT:
225
                    $parts[] = 'EXCEPT' . $modifier . PHP_EOL . $select;
226
                    break;
227
            }
228
        }
229
230
        return $parts;
231
    }
232
233
    protected function getOuterOrderBy(): array
234
    {
235
        if (count($this->outerOrderByParts) === 0) {
236
            return [];
237
        }
238
239
        $parts = [];
240
        foreach ($this->outerOrderByParts as $column => $direction) {
241
            $parts[] = "$column $direction";
242
        }
243
244
        return ['ORDER BY ' . implode(', ', $parts)];
245
    }
246
247
    protected function getOuterLimit(): array
248
    {
249
        $parts = [];
250
        if ($this->outerLimit !== null && $this->outerOffset !== null) {
251
            $parts[] = 'LIMIT ' . $this->outerOffset . ', ' . $this->outerLimit;
252
        } elseif ($this->outerLimit !== null) {
253
            $parts[] = 'LIMIT ' . $this->outerLimit;
254
        }
255
256
        return $parts;
257
    }
258
}
259