Passed
Push — master ( 1dcf9b...c6a312 )
by y
01:25
created

ComparisonTrait::is()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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