Passed
Push — master ( 8ed35d...e890e8 )
by y
01:43
created

ComparisonTrait::isGtAny()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 2
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Helix\DB\SQL;
4
5
use Helix\DB\Select;
6
7
/**
8
 * Produces comparative expressions for the instance.
9
 *
10
 * Because SQLite lacks the `ANY` and `ALL` comparison operators,
11
 * subqueries are instead nested and correlated using `EXISTS` or `NOT EXISTS`.
12
 *
13
 * This also requires that the subquery's first column is referable.
14
 */
15
trait ComparisonTrait {
16
17
    use AbstractTrait;
18
19
    /**
20
     * Relational comparison helper.
21
     *
22
     * @param number|string|Select $arg
23
     * @param string $oper Relational operator
24
     * @param string $multi `ALL|ANY`
25
     * @return Predicate
26
     * @internal
27
     */
28
    protected function _isRelational ($arg, string $oper, string $multi) {
29
        static $inverse = [
30
            '<' => '>=',
31
            '<=' => '>',
32
            '>=' => '<',
33
            '>' => '<='
34
        ];
35
        if ($arg instanceof Select) {
36
            if ($this->db->isSQLite()) {
37
                $sub = Select::factory($this->db, $arg, [$arg[0]]);
38
                if ($multi === 'ANY') {
39
                    return $sub->where("{$this} {$oper} {$arg[0]}")->isNotEmpty();
40
                }
41
                return $sub->where("{$this} {$inverse[$oper]} {$arg[0]}")->isEmpty();
42
            }
43
            return Predicate::factory($this->db, "{$this} {$oper} {$multi} ({$arg->toSql()})");
44
        }
45
        return Predicate::factory($this->db, "{$this} {$oper} {$this->db->quote($arg)}");
46
    }
47
48
    /**
49
     * Null-safe equality.
50
     *
51
     * - Mysql: `$this <=> $arg`, or `$this <=> ANY ($arg)`
52
     * - SQLite: `$this IS $arg`, or `EXISTS (... WHERE $this IS $arg[0])`
53
     *
54
     * @param null|scalar|Select $arg
55
     * @return Predicate
56
     */
57
    public function is ($arg): Predicate {
58
        if ($arg instanceof Select) {
59
            if ($this->db->isSQLite()) {
60
                return Select::factory($this->db, $arg, [$arg[0]])
61
                    ->where("{$this} IS {$arg[0]}")
62
                    ->isNotEmpty();
63
            }
64
            return Predicate::factory($this->db, "{$this} <=> ANY ({$arg->toSql()})");
65
        }
66
        if ($arg === null or is_bool($arg)) {
67
            $arg = ['' => 'NULL', 1 => 'TRUE', 0 => 'FALSE'][$arg];
68
        }
69
        else {
70
            $arg = $this->db->quote($arg);
71
        }
72
        if ($this->db->isMySQL()) {
73
            return Predicate::factory($this->db, "{$this} <=> {$arg}");
74
        }
75
        return Predicate::factory($this->db, "{$this} IS {$arg}");
76
    }
77
78
    /**
79
     * `$this BETWEEN $min AND $max` (inclusive)
80
     *
81
     * @param number $min
82
     * @param number $max
83
     * @return Predicate
84
     */
85
    public function isBetween ($min, $max) {
86
        $min = $this->db->quote($min);
87
        $max = $this->db->quote($max);
88
        return Predicate::factory($this->db, "{$this} BETWEEN {$min} AND {$max}");
89
    }
90
91
    /**
92
     * `$this = $arg` or `$this IN ($arg)`
93
     *
94
     * @param scalar|array|Select $arg
95
     * @return Predicate
96
     */
97
    public function isEqual ($arg) {
98
        return $this->db->match($this, $arg);
99
    }
100
101
    /**
102
     * `$this IS FALSE`
103
     *
104
     * @return Predicate
105
     */
106
    public function isFalse () {
107
        return Predicate::factory($this->db, "{$this} IS FALSE");
108
    }
109
110
    /**
111
     * `$this > $arg`, or driver-specific `$this > ALL (SELECT ...)`
112
     *
113
     * @param number|string|Select $arg
114
     * @return Predicate
115
     */
116
    public function isGt ($arg) {
117
        return $this->_isRelational($arg, '>', 'ALL');
118
    }
119
120
    /**
121
     * Driver-appropriate `$this > ANY (SELECT ...)`
122
     *
123
     * @param Select $select
124
     * @return Predicate
125
     */
126
    public function isGtAny (Select $select) {
127
        return $this->_isRelational($select, '>', 'ANY');
128
    }
129
130
    /**
131
     * `$this >= $arg`, or driver-appropriate `$this >= ALL (SELECT ...)`
132
     *
133
     * @param number|string|Select $arg
134
     * @return Predicate
135
     */
136
    public function isGte ($arg) {
137
        return $this->_isRelational($arg, '>=', 'ALL');
138
    }
139
140
    /**
141
     * Driver-appropriate `$this >= ANY (SELECT ...)`
142
     *
143
     * @param Select $select
144
     * @return Predicate
145
     */
146
    public function isGteAny (Select $select) {
147
        return $this->_isRelational($select, '>=', 'ANY');
148
    }
149
150
    /**
151
     * `$this LIKE $pattern`
152
     *
153
     * @param string $pattern
154
     * @return Predicate
155
     */
156
    public function isLike (string $pattern) {
157
        $pattern = $this->db->quote($pattern);
158
        return Predicate::factory($this->db, "{$this} LIKE {$pattern}");
159
    }
160
161
    /**
162
     * `$this < $arg`, or driver-appropriate `$this < ALL (SELECT ...)`
163
     *
164
     * @param number|string|Select $arg
165
     * @return Predicate
166
     */
167
    public function isLt ($arg) {
168
        return $this->_isRelational($arg, '<', 'ALL');
169
    }
170
171
    /**
172
     * Driver-appropriate `$this < ANY (SELECT ...)`
173
     *
174
     * @param Select $select
175
     * @return Predicate
176
     */
177
    public function isLtAny (Select $select) {
178
        return $this->_isRelational($select, '<', 'ANY');
179
    }
180
181
    /**
182
     * `$this <= $arg`, or driver-appropriate `$this <= ALL (SELECT ...)`
183
     *
184
     * @param number|string|Select $arg
185
     * @return Predicate
186
     */
187
    public function isLte ($arg) {
188
        return $this->_isRelational($arg, '<=', 'ALL');
189
    }
190
191
    /**
192
     * Driver-appropriate `$this <= ANY (SELECT ...)`
193
     *
194
     * @param Select $select
195
     * @return Predicate
196
     */
197
    public function isLteAny (Select $select) {
198
        return $this->_isRelational($select, '<=', 'ANY');
199
    }
200
201
    /**
202
     * Null-safe inequality.
203
     *
204
     * @param null|scalar|Select $arg
205
     * @return Predicate
206
     */
207
    public function isNot ($arg) {
208
        return $this->is($arg)->not();
209
    }
210
211
    /**
212
     * `$this NOT BETWEEN $min AND $max` (inclusive)
213
     *
214
     * @param number $min
215
     * @param number $max
216
     * @return Predicate
217
     */
218
    public function isNotBetween ($min, $max) {
219
        $min = $this->db->quote($min);
220
        $max = $this->db->quote($max);
221
        return Predicate::factory($this->db, "{$this} NOT BETWEEN {$min} AND {$max}");
222
    }
223
224
    /**
225
     * `$this <> $arg` or `$this NOT IN ($arg)`
226
     *
227
     * @param scalar|array|Select $arg
228
     * @return Predicate
229
     */
230
    public function isNotEqual ($arg) {
231
        if ($arg instanceof Select) {
232
            return Predicate::factory($this->db, "{$this} NOT IN ({$arg->toSql()})");
233
        }
234
        if (is_array($arg)) {
235
            return Predicate::factory($this->db, "{$this} NOT IN ({$this->db->quoteList($arg)})");
236
        }
237
        return Predicate::factory($this->db, "{$this} <> {$this->db->quote($arg)}");
238
    }
239
240
    /**
241
     * `$this NOT LIKE $pattern`
242
     *
243
     * @param string $pattern
244
     * @return Predicate
245
     */
246
    public function isNotLike (string $pattern) {
247
        $pattern = $this->db->quote($pattern);
248
        return Predicate::factory($this->db, "{$this} NOT LIKE {$pattern}");
249
    }
250
251
    /**
252
     * `$this IS NOT NULL`
253
     *
254
     * @return Predicate
255
     */
256
    public function isNotNull () {
257
        return Predicate::factory($this->db, "{$this} IS NOT NULL");
258
    }
259
260
    /**
261
     * `$this NOT REGEXP $pattern`
262
     *
263
     * @param string $pattern
264
     * @return Predicate
265
     */
266
    public function isNotRegExp (string $pattern) {
267
        $pattern = $this->db->quote($pattern);
268
        return Predicate::factory($this->db, "{$this} NOT REGEXP {$pattern}");
269
    }
270
271
    /**
272
     * `$this IS NULL`
273
     *
274
     * @return Predicate
275
     */
276
    public function isNull () {
277
        return Predicate::factory($this->db, "{$this} IS NULL");
278
    }
279
280
    /**
281
     * `$this REGEXP $pattern`
282
     *
283
     * @param string $pattern
284
     * @return Predicate
285
     */
286
    public function isRegExp (string $pattern) {
287
        $pattern = $this->db->quote($pattern);
288
        return Predicate::factory($this->db, "{$this} REGEXP {$pattern}");
289
    }
290
291
    /**
292
     * `CASE $this ... END`
293
     *
294
     * > :warning: If `$values` are given, the keys are quoted as literal values.
295
     * > Omit `$values` and use {@link Choice::when()} if you need expressions for the `WHEN` clause.
296
     *
297
     * @param array $values `[when => then]`
298
     * @return Choice
299
     */
300
    public function switch (array $values = []) {
301
        return Choice::factory($this->db, "{$this}", $values);
302
    }
303
}