Passed
Push — master ( 914087...c6011d )
by Alexander
11:22
created

QueryBuilderProvider::buildConditionsProvider()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 373
Code Lines 217

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 217
nc 2
nop 0
dl 0
loc 373
rs 8
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\TestSupport\Provider;
6
7
use Yiisoft\Db\Connection\ConnectionInterface;
8
use Yiisoft\Db\Expression\Expression;
9
use Yiisoft\Db\QueryBuilder\Conditions\BetweenColumnsCondition;
0 ignored issues
show
Bug introduced by
The type Yiisoft\Db\QueryBuilder\...BetweenColumnsCondition was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use Yiisoft\Db\QueryBuilder\Conditions\InCondition;
0 ignored issues
show
Bug introduced by
The type Yiisoft\Db\QueryBuilder\Conditions\InCondition was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
use Yiisoft\Db\QueryBuilder\Conditions\LikeCondition;
0 ignored issues
show
Bug introduced by
The type Yiisoft\Db\QueryBuilder\Conditions\LikeCondition was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use Yiisoft\Db\QueryBuilder\QueryBuilder;
13
use Yiisoft\Db\QueryBuilder\QueryBuilderInterface;
14
use Yiisoft\Db\Query\Query;
15
use Yiisoft\Db\TestSupport\Helper\DbHelper;
16
use Yiisoft\Db\TestSupport\TraversableObject;
17
18
final class QueryBuilderProvider
19
{
20
    public function __construct(
21
        private ConnectionInterface $db,
22
        private string $likeEscapeCharSql = '',
23
        private array $likeParameterReplacements = []
24
    ) {
25
    }
26
27
    public function addDropForeignKeysProvider(): array
28
    {
29
        $name = 'CN_constraints_3';
30
        $pkTableName = 'T_constraints_2';
31
        $tableName = 'T_constraints_3';
32
33
        return [
34
            'drop' => [
35
                "ALTER TABLE {{{$tableName}}} DROP CONSTRAINT [[$name]]",
36
                static function (QueryBuilderInterface $qb) use ($tableName, $name) {
37
                    return $qb->dropForeignKey($name, $tableName);
38
                },
39
            ],
40
            'add' => [
41
                "ALTER TABLE {{{$tableName}}} ADD CONSTRAINT [[$name]] FOREIGN KEY ([[C_fk_id_1]])"
42
                . " REFERENCES {{{$pkTableName}}} ([[C_id_1]]) ON DELETE CASCADE ON UPDATE CASCADE",
43
                static function (QueryBuilderInterface $qb) use ($tableName, $name, $pkTableName) {
44
                    return $qb->addForeignKey(
45
                        $name,
46
                        $tableName,
47
                        'C_fk_id_1',
48
                        $pkTableName,
49
                        'C_id_1',
50
                        'CASCADE',
51
                        'CASCADE'
52
                    );
53
                },
54
            ],
55
            'add (2 columns)' => [
56
                "ALTER TABLE {{{$tableName}}} ADD CONSTRAINT [[$name]] FOREIGN KEY ([[C_fk_id_1]], [[C_fk_id_2]])"
57
                . " REFERENCES {{{$pkTableName}}} ([[C_id_1]], [[C_id_2]]) ON DELETE CASCADE ON UPDATE CASCADE",
58
                static function (QueryBuilderInterface $qb) use ($tableName, $name, $pkTableName) {
59
                    return $qb->addForeignKey(
60
                        $name,
61
                        $tableName,
62
                        'C_fk_id_1, C_fk_id_2',
63
                        $pkTableName,
64
                        'C_id_1, C_id_2',
65
                        'CASCADE',
66
                        'CASCADE'
67
                    );
68
                },
69
            ],
70
        ];
71
    }
72
73
    public function addDropPrimaryKeysProvider(): array
74
    {
75
        $tableName = 'T_constraints_1';
76
        $name = 'CN_pk';
77
78
        return [
79
            'drop' => [
80
                "ALTER TABLE {{{$tableName}}} DROP CONSTRAINT [[$name]]",
81
                static function (QueryBuilderInterface $qb) use ($tableName, $name) {
82
                    return $qb->dropPrimaryKey($name, $tableName);
83
                },
84
            ],
85
            'add' => [
86
                "ALTER TABLE {{{$tableName}}} ADD CONSTRAINT [[$name]] PRIMARY KEY ([[C_id_1]])",
87
                static function (QueryBuilderInterface $qb) use ($tableName, $name) {
88
                    return $qb->addPrimaryKey($name, $tableName, 'C_id_1');
89
                },
90
            ],
91
            'add (2 columns)' => [
92
                "ALTER TABLE {{{$tableName}}} ADD CONSTRAINT [[$name]] PRIMARY KEY ([[C_id_1]], [[C_id_2]])",
93
                static function (QueryBuilderInterface $qb) use ($tableName, $name) {
94
                    return $qb->addPrimaryKey($name, $tableName, 'C_id_1, C_id_2');
95
                },
96
            ],
97
        ];
98
    }
99
100
    public function addDropUniquesProvider(): array
101
    {
102
        $tableName1 = 'T_constraints_1';
103
        $name1 = 'CN_unique';
104
        $tableName2 = 'T_constraints_2';
105
        $name2 = 'CN_constraints_2_multi';
106
107
        return [
108
            'drop' => [
109
                "ALTER TABLE {{{$tableName1}}} DROP CONSTRAINT [[$name1]]",
110
                static function (QueryBuilderInterface $qb) use ($tableName1, $name1) {
111
                    return $qb->dropUnique($name1, $tableName1);
112
                },
113
            ],
114
            'add' => [
115
                "ALTER TABLE {{{$tableName1}}} ADD CONSTRAINT [[$name1]] UNIQUE ([[C_unique]])",
116
                static function (QueryBuilderInterface $qb) use ($tableName1, $name1) {
117
                    return $qb->addUnique($name1, $tableName1, 'C_unique');
118
                },
119
            ],
120
            'add (2 columns)' => [
121
                "ALTER TABLE {{{$tableName2}}} ADD CONSTRAINT [[$name2]] UNIQUE ([[C_index_2_1]], [[C_index_2_2]])",
122
                static function (QueryBuilderInterface $qb) use ($tableName2, $name2) {
123
                    return $qb->addUnique($name2, $tableName2, 'C_index_2_1, C_index_2_2');
124
                },
125
            ],
126
        ];
127
    }
128
129
    public function batchInsertProvider(): array
130
    {
131
        return [
132
            [
133
                'customer',
134
                ['email', 'name', 'address'],
135
                [['[email protected]', 'silverfire', 'Kyiv {{city}}, Ukraine']],
136
                DbHelper::replaceQuotes(
137
                    'INSERT INTO [[customer]] ([[email]], [[name]], [[address]])'
138
                    . " VALUES ('[email protected]', 'silverfire', 'Kyiv {{city}}, Ukraine')",
139
                    $this->db->getDriverName(),
140
                ),
141
            ],
142
            'escape-danger-chars' => [
143
                'customer',
144
                ['address'],
145
                [["SQL-danger chars are escaped: '); --"]],
146
                'expected' => DbHelper::replaceQuotes(
147
                    "INSERT INTO [[customer]] ([[address]]) VALUES ('SQL-danger chars are escaped: \'); --')",
148
                    $this->db->getDriverName(),
149
                ),
150
            ],
151
            [
152
                'customer',
153
                ['address'],
154
                [],
155
                '',
156
            ],
157
            [
158
                'customer',
159
                [],
160
                [['no columns passed']],
161
                DbHelper::replaceQuotes(
162
                    "INSERT INTO [[customer]] () VALUES ('no columns passed')",
163
                    $this->db->getDriverName(),
164
                ),
165
            ],
166
            'bool-false, bool2-null' => [
167
                'type',
168
                ['bool_col', 'bool_col2'],
169
                [[false, null]],
170
                'expected' => DbHelper::replaceQuotes(
171
                    'INSERT INTO [[type]] ([[bool_col]], [[bool_col2]]) VALUES (0, NULL)',
172
                    $this->db->getDriverName(),
173
                ),
174
            ],
175
            [
176
                '{{%type}}',
177
                ['{{%type}}.[[float_col]]', '[[time]]'],
178
                [[null, new Expression('now()')]],
179
                'INSERT INTO {{%type}} ({{%type}}.[[float_col]], [[time]]) VALUES (NULL, now())',
180
            ],
181
            'bool-false, time-now()' => [
182
                '{{%type}}',
183
                ['{{%type}}.[[bool_col]]', '[[time]]'],
184
                [[false, new Expression('now()')]],
185
                'expected' => 'INSERT INTO {{%type}} ({{%type}}.[[bool_col]], [[time]]) VALUES (0, now())',
186
            ],
187
        ];
188
    }
189
190
    public function buildConditionsProvider(): array
191
    {
192
        $conditions = [
193
            /* empty values */
194
            [['like', 'name', []], '0=1', []],
195
            [['not like', 'name', []], '', []],
196
            [['or like', 'name', []], '0=1', []],
197
            [['or not like', 'name', []], '', []],
198
199
            /* not */
200
            [['not', 'name'], 'NOT (name)', []],
201
            [
202
                [
203
                    'not',
204
                    (new Query($this->db))
205
                        ->select('exists')
206
                        ->from('some_table'),
207
                ],
208
                'NOT ((SELECT [[exists]] FROM [[some_table]]))', [],
209
            ],
210
211
            /* and */
212
            [['and', 'id=1', 'id=2'], '(id=1) AND (id=2)', []],
213
            [['and', 'type=1', ['or', 'id=1', 'id=2']], '(type=1) AND ((id=1) OR (id=2))', []],
214
            [['and', 'id=1', new Expression('id=:qp0', [':qp0' => 2])], '(id=1) AND (id=:qp0)', [':qp0' => 2]],
215
            [
216
                [
217
                    'and',
218
                    ['expired' => false],
219
                    (new Query($this->db))
220
                        ->select('count(*) > 1')
221
                        ->from('queue'),
222
                ],
223
                '([[expired]]=:qp0) AND ((SELECT count(*) > 1 FROM [[queue]]))',
224
                [':qp0' => false],
225
            ],
226
227
            /* or */
228
            [['or', 'id=1', 'id=2'], '(id=1) OR (id=2)', []],
229
            [['or', 'type=1', ['or', 'id=1', 'id=2']], '(type=1) OR ((id=1) OR (id=2))', []],
230
            [['or', 'type=1', new Expression('id=:qp0', [':qp0' => 1])], '(type=1) OR (id=:qp0)', [':qp0' => 1]],
231
232
            /* between */
233
            [['between', 'id', 1, 10], '[[id]] BETWEEN :qp0 AND :qp1', [':qp0' => 1, ':qp1' => 10]],
234
            [['not between', 'id', 1, 10], '[[id]] NOT BETWEEN :qp0 AND :qp1', [':qp0' => 1, ':qp1' => 10]],
235
            [
236
                ['between', 'date', new Expression('(NOW() - INTERVAL 1 MONTH)'), new Expression('NOW()')],
237
                '[[date]] BETWEEN (NOW() - INTERVAL 1 MONTH) AND NOW()',
238
                [],
239
            ],
240
            [
241
                ['between', 'date', new Expression('(NOW() - INTERVAL 1 MONTH)'), 123],
242
                '[[date]] BETWEEN (NOW() - INTERVAL 1 MONTH) AND :qp0',
243
                [':qp0' => 123],
244
            ],
245
            [
246
                ['not between', 'date', new Expression('(NOW() - INTERVAL 1 MONTH)'), new Expression('NOW()')],
247
                '[[date]] NOT BETWEEN (NOW() - INTERVAL 1 MONTH) AND NOW()',
248
                [],
249
            ],
250
            [
251
                ['not between', 'date', new Expression('(NOW() - INTERVAL 1 MONTH)'), 123],
252
                '[[date]] NOT BETWEEN (NOW() - INTERVAL 1 MONTH) AND :qp0',
253
                [':qp0' => 123],
254
            ],
255
            [
256
                new BetweenColumnsCondition('2018-02-11', 'BETWEEN', 'create_time', 'update_time'),
257
                ':qp0 BETWEEN [[create_time]] AND [[update_time]]',
258
                [':qp0' => '2018-02-11'],
259
            ],
260
            [
261
                new BetweenColumnsCondition('2018-02-11', 'NOT BETWEEN', 'NOW()', 'update_time'),
262
                ':qp0 NOT BETWEEN NOW() AND [[update_time]]',
263
                [':qp0' => '2018-02-11'],
264
            ],
265
            [
266
                new BetweenColumnsCondition(new Expression('NOW()'), 'BETWEEN', 'create_time', 'update_time'),
267
                'NOW() BETWEEN [[create_time]] AND [[update_time]]',
268
                [],
269
            ],
270
            [
271
                new BetweenColumnsCondition(new Expression('NOW()'), 'NOT BETWEEN', 'create_time', 'update_time'),
272
                'NOW() NOT BETWEEN [[create_time]] AND [[update_time]]',
273
                [],
274
            ],
275
            [
276
                new BetweenColumnsCondition(
277
                    new Expression('NOW()'),
278
                    'NOT BETWEEN',
279
                    (new Query($this->db))
280
                        ->select('min_date')
281
                        ->from('some_table'),
282
                    'max_date'
283
                ),
284
                'NOW() NOT BETWEEN (SELECT [[min_date]] FROM [[some_table]]) AND [[max_date]]',
285
                [],
286
            ],
287
288
            /* in */
289
            [
290
                [
291
                    'in',
292
                    'id',
293
                    [
294
                        1,
295
                        2,
296
                        (new Query($this->db))
297
                            ->select('three')
298
                            ->from('digits'),
299
                    ],
300
                ],
301
                '[[id]] IN (:qp0, :qp1, (SELECT [[three]] FROM [[digits]]))',
302
                [':qp0' => 1, ':qp1' => 2],
303
            ],
304
            [
305
                ['not in', 'id', [1, 2, 3]],
306
                '[[id]] NOT IN (:qp0, :qp1, :qp2)',
307
                [':qp0' => 1, ':qp1' => 2, ':qp2' => 3],
308
            ],
309
            [
310
                [
311
                    'in',
312
                    'id',
313
                    (new Query($this->db))
314
                        ->select('id')
315
                        ->from('users')
316
                        ->where(['active' => 1]),
317
                ],
318
                '[[id]] IN (SELECT [[id]] FROM [[users]] WHERE [[active]]=:qp0)',
319
                [':qp0' => 1],
320
            ],
321
            [
322
                [
323
                    'not in',
324
                    'id',
325
                    (new Query($this->db))
326
                        ->select('id')
327
                        ->from('users')
328
                        ->where(['active' => 1]),
329
                ],
330
                '[[id]] NOT IN (SELECT [[id]] FROM [[users]] WHERE [[active]]=:qp0)',
331
                [':qp0' => 1],
332
            ],
333
            [['in', 'id', 1], '[[id]]=:qp0', [':qp0' => 1]],
334
            [['in', 'id', [1]], '[[id]]=:qp0', [':qp0' => 1]],
335
            [['in', 'id', new TraversableObject([1])], '[[id]]=:qp0', [':qp0' => 1]],
336
            'composite in' => [
337
                ['in', ['id', 'name'], [['id' => 1, 'name' => 'oy']]],
338
                '([[id]], [[name]]) IN ((:qp0, :qp1))',
339
                [':qp0' => 1, ':qp1' => 'oy'],
340
            ],
341
            'composite in (just one column)' => [
342
                ['in', ['id'], [['id' => 1, 'name' => 'Name1'], ['id' => 2, 'name' => 'Name2']]],
343
                '[[id]] IN (:qp0, :qp1)',
344
                [':qp0' => 1, ':qp1' => 2],
345
            ],
346
            'composite in using array objects (just one column)' => [
347
                ['in', new TraversableObject(['id']), new TraversableObject([
348
                    ['id' => 1, 'name' => 'Name1'],
349
                    ['id' => 2, 'name' => 'Name2'],
350
                ])],
351
                '[[id]] IN (:qp0, :qp1)',
352
                [':qp0' => 1, ':qp1' => 2],
353
            ],
354
355
            /* in using array objects. */
356
            [['id' => new TraversableObject([1, 2])], '[[id]] IN (:qp0, :qp1)', [':qp0' => 1, ':qp1' => 2]],
357
            [
358
                ['in', 'id', new TraversableObject([1, 2, 3])],
359
                '[[id]] IN (:qp0, :qp1, :qp2)',
360
                [':qp0' => 1, ':qp1' => 2, ':qp2' => 3],
361
            ],
362
363
            /* in using array objects containing null value */
364
            [['in', 'id', new TraversableObject([1, null])], '[[id]]=:qp0 OR [[id]] IS NULL', [':qp0' => 1]],
365
            [
366
                ['in', 'id', new TraversableObject([1, 2, null])],
367
                '[[id]] IN (:qp0, :qp1) OR [[id]] IS NULL', [':qp0' => 1, ':qp1' => 2],
368
            ],
369
370
            /* not in using array object containing null value */
371
            [
372
                ['not in', 'id', new TraversableObject([1, null])],
373
                '[[id]]<>:qp0 AND [[id]] IS NOT NULL', [':qp0' => 1],
374
            ],
375
            [
376
                ['not in', 'id', new TraversableObject([1, 2, null])],
377
                '[[id]] NOT IN (:qp0, :qp1) AND [[id]] IS NOT NULL',
378
                [':qp0' => 1, ':qp1' => 2],
379
            ],
380
381
            /* in using array object containing only null value */
382
            [['in', 'id', new TraversableObject([null])], '[[id]] IS NULL', []],
383
            [['not in', 'id', new TraversableObject([null])], '[[id]] IS NOT NULL', []],
384
            'composite in using array objects' => [
385
                ['in', new TraversableObject(['id', 'name']), new TraversableObject([
386
                    ['id' => 1, 'name' => 'oy'],
387
                    ['id' => 2, 'name' => 'yo'],
388
                ])],
389
                '([[id]], [[name]]) IN ((:qp0, :qp1), (:qp2, :qp3))',
390
                [':qp0' => 1, ':qp1' => 'oy', ':qp2' => 2, ':qp3' => 'yo'],
391
            ],
392
393
            /* in object conditions */
394
            [new InCondition('id', 'in', 1), '[[id]]=:qp0', [':qp0' => 1]],
395
            [new InCondition('id', 'in', [1]), '[[id]]=:qp0', [':qp0' => 1]],
396
            [new InCondition('id', 'not in', 1), '[[id]]<>:qp0', [':qp0' => 1]],
397
            [new InCondition('id', 'not in', [1]), '[[id]]<>:qp0', [':qp0' => 1]],
398
            [new InCondition('id', 'in', [1, 2]), '[[id]] IN (:qp0, :qp1)', [':qp0' => 1, ':qp1' => 2]],
399
            [new InCondition('id', 'not in', [1, 2]), '[[id]] NOT IN (:qp0, :qp1)', [':qp0' => 1, ':qp1' => 2]],
400
401
            /* exists */
402
            [
403
                [
404
                    'exists',
405
                    (new Query($this->db))
406
                        ->select('id')
407
                        ->from('users')
408
                        ->where(['active' => 1]),
409
                ],
410
                'EXISTS (SELECT [[id]] FROM [[users]] WHERE [[active]]=:qp0)',
411
                [':qp0' => 1],
412
            ],
413
            [
414
                [
415
                    'not exists',
416
                    (new Query($this->db))
417
                        ->select('id')
418
                        ->from('users')
419
                        ->where(['active' => 1]),
420
                ],
421
                'NOT EXISTS (SELECT [[id]] FROM [[users]] WHERE [[active]]=:qp0)', [':qp0' => 1],
422
            ],
423
424
            /* simple conditions */
425
            [['=', 'a', 'b'], '[[a]] = :qp0', [':qp0' => 'b']],
426
            [['>', 'a', 1], '[[a]] > :qp0', [':qp0' => 1]],
427
            [['>=', 'a', 'b'], '[[a]] >= :qp0', [':qp0' => 'b']],
428
            [['<', 'a', 2], '[[a]] < :qp0', [':qp0' => 2]],
429
            [['<=', 'a', 'b'], '[[a]] <= :qp0', [':qp0' => 'b']],
430
            [['<>', 'a', 3], '[[a]] <> :qp0', [':qp0' => 3]],
431
            [['!=', 'a', 'b'], '[[a]] != :qp0', [':qp0' => 'b']],
432
            [
433
                ['>=', 'date', new Expression('DATE_SUB(NOW(), INTERVAL 1 MONTH)')],
434
                '[[date]] >= DATE_SUB(NOW(), INTERVAL 1 MONTH)',
435
                [],
436
            ],
437
            [
438
                ['>=', 'date', new Expression('DATE_SUB(NOW(), INTERVAL :month MONTH)', [':month' => 2])],
439
                '[[date]] >= DATE_SUB(NOW(), INTERVAL :month MONTH)',
440
                [':month' => 2],
441
            ],
442
            [
443
                [
444
                    '=',
445
                    'date',
446
                    (new Query($this->db))
447
                        ->select('max(date)')
448
                        ->from('test')
449
                        ->where(['id' => 5]),
450
                ],
451
                '[[date]] = (SELECT max(date) FROM [[test]] WHERE [[id]]=:qp0)',
452
                [':qp0' => 5],
453
            ],
454
455
            /* operand1 is Expression */
456
            [
457
                ['=', new Expression('date'), '2019-08-01'],
458
                'date = :qp0',
459
                [':qp0' => '2019-08-01'],
460
            ],
461
            [
462
                [
463
                    '=',
464
                    (new Query($this->db))
465
                        ->select('COUNT(*)')
466
                        ->from('test')
467
                        ->where(['id' => 6]),
468
                    0,
469
                ],
470
                '(SELECT COUNT(*) FROM [[test]] WHERE [[id]]=:qp0) = :qp1',
471
                [':qp0' => 6, ':qp1' => 0],
472
            ],
473
474
            /* hash condition */
475
            [['a' => 1, 'b' => 2], '([[a]]=:qp0) AND ([[b]]=:qp1)', [':qp0' => 1, ':qp1' => 2]],
476
            [
477
                ['a' => new Expression('CONCAT(col1, col2)'), 'b' => 2],
478
                '([[a]]=CONCAT(col1, col2)) AND ([[b]]=:qp0)',
479
                [':qp0' => 2],
480
            ],
481
482
            /* direct conditions */
483
            ['a = CONCAT(col1, col2)', 'a = CONCAT(col1, col2)', []],
484
            [
485
                new Expression('a = CONCAT(col1, :param1)', ['param1' => 'value1']),
486
                'a = CONCAT(col1, :param1)',
487
                ['param1' => 'value1'],
488
            ],
489
490
            /* Expression with params as operand of 'not' */
491
            [
492
                ['not', new Expression('any_expression(:a)', [':a' => 1])],
493
                'NOT (any_expression(:a))', [':a' => 1],
494
            ],
495
            [new Expression('NOT (any_expression(:a))', [':a' => 1]), 'NOT (any_expression(:a))', [':a' => 1]],
496
        ];
497
498
        $conditions = match ($this->db->getDriverName()) {
499
            'sqlsrv', 'sqlite' => array_merge($conditions, [
500
                [
501
                    ['in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']]],
502
                    '(([[id]] = :qp0 AND [[name]] = :qp1) OR ([[id]] = :qp2 AND [[name]] = :qp3))',
503
                    [':qp0' => 1,
504
                        ':qp1' => 'foo',
505
                        ':qp2' => 2,
506
                        ':qp3' => 'bar',],
507
                ],
508
                [
509
                    ['not in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']]],
510
                    '(([[id]] != :qp0 OR [[name]] != :qp1) AND ([[id]] != :qp2 OR [[name]] != :qp3))',
511
                    [':qp0' => 1,
512
                        ':qp1' => 'foo',
513
                        ':qp2' => 2,
514
                        ':qp3' => 'bar',],
515
                ],
516
                //[['in', ['id', 'name'], (new Query())->select(['id', 'name'])->from('users')->where(['active' => 1])], 'EXISTS (SELECT 1 FROM (SELECT [[id]], [[name]] FROM [[users]] WHERE [[active]]=:qp0) AS a WHERE a.[[id]] = [[id AND a.]]name[[ = ]]name`)', [':qp0' => 1] ],
517
                //[ ['not in', ['id', 'name'], (new Query())->select(['id', 'name'])->from('users')->where(['active' => 1])], 'NOT EXISTS (SELECT 1 FROM (SELECT [[id]], [[name]] FROM [[users]] WHERE [[active]]=:qp0) AS a WHERE a.[[id]] = [[id]] AND a.[[name = ]]name`)', [':qp0' => 1] ],
518
            ]),
519
            default => array_merge($conditions, [
520
                [
521
                    ['in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']]],
522
                    '([[id]], [[name]]) IN ((:qp0, :qp1), (:qp2, :qp3))',
523
                    [':qp0' => 1, ':qp1' => 'foo', ':qp2' => 2, ':qp3' => 'bar'],
524
                ],
525
                [
526
                    ['not in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']]],
527
                    '([[id]], [[name]]) NOT IN ((:qp0, :qp1), (:qp2, :qp3))',
528
                    [':qp0' => 1, ':qp1' => 'foo', ':qp2' => 2, ':qp3' => 'bar'],
529
                ],
530
                [
531
                    [
532
                        'in',
533
                        ['id', 'name'],
534
                        (new Query($this->db))
535
                            ->select(['id', 'name'])
536
                            ->from('users')
537
                            ->where(['active' => 1]),
538
                    ],
539
                    '([[id]], [[name]]) IN (SELECT [[id]], [[name]] FROM [[users]] WHERE [[active]]=:qp0)',
540
                    [':qp0' => 1],
541
                ],
542
                [
543
                    [
544
                        'not in',
545
                        ['id', 'name'],
546
                        (new Query($this->db))
547
                            ->select(['id', 'name'])
548
                            ->from('users')
549
                            ->where(['active' => 1]),
550
                    ],
551
                    '([[id]], [[name]]) NOT IN (SELECT [[id]], [[name]] FROM [[users]] WHERE [[active]]=:qp0)',
552
                    [':qp0' => 1],
553
                ],
554
            ]),
555
        };
556
557
        /* adjust dbms specific escaping */
558
        foreach ($conditions as $i => $condition) {
559
            $conditions[$i][1] = DbHelper::replaceQuotes($condition[1], $this->db->getDriverName());
560
        }
561
562
        return $conditions;
563
    }
564
565
    public function buildExistsParamsProvider(): array
566
    {
567
        return [
568
            [
569
                'exists',
570
                DbHelper::replaceQuotes(
571
                    'SELECT [[id]] FROM [[TotalExample]] [[t]] WHERE EXISTS (SELECT [[1]] FROM [[Website]] [[w]])',
572
                    $this->db->getDriverName()
573
                ),
574
            ],
575
            [
576
                'not exists',
577
                DbHelper::replaceQuotes(
578
                    'SELECT [[id]] FROM [[TotalExample]] [[t]] WHERE NOT EXISTS (SELECT [[1]] FROM [[Website]] [[w]])',
579
                    $this->db->getDriverName()
580
                ),
581
            ],
582
        ];
583
    }
584
585
    public function buildFilterConditionProvider(): array
586
    {
587
        $conditions = [
588
            /* like */
589
            [['like', 'name', []], '', []],
590
            [['not like', 'name', []], '', []],
591
            [['or like', 'name', []], '', []],
592
            [['or not like', 'name', []], '', []],
593
594
            /* not */
595
            [['not', ''], '', []],
596
597
            /* and */
598
            [['and', '', ''], '', []],
599
            [['and', '', 'id=2'], 'id=2', []],
600
            [['and', 'id=1', ''], 'id=1', []],
601
            [['and', 'type=1', ['or', '', 'id=2']], '(type=1) AND (id=2)', []],
602
603
            /* or */
604
            [['or', 'id=1', ''], 'id=1', []],
605
            [['or', 'type=1', ['or', '', 'id=2']], '(type=1) OR (id=2)', []],
606
607
            /* between */
608
            [['between', 'id', 1, null], '', []],
609
            [['not between', 'id', null, 10], '', []],
610
611
            /* in */
612
            [['in', 'id', []], '', []],
613
            [['not in', 'id', []], '', []],
614
615
            /* simple conditions */
616
            [['=', 'a', ''], '', []],
617
            [['>', 'a', ''], '', []],
618
            [['>=', 'a', ''], '', []],
619
            [['<', 'a', ''], '', []],
620
            [['<=', 'a', ''], '', []],
621
            [['<>', 'a', ''], '', []],
622
            [['!=', 'a', ''], '', []],
623
        ];
624
625
        /* adjust dbms specific escaping */
626
        foreach ($conditions as $i => $condition) {
627
            $conditions[$i][1] = DbHelper::replaceQuotes($condition[1], $this->db->getDriverName());
628
        }
629
630
        return $conditions;
631
    }
632
633
    public function buildFromDataProvider(): array
634
    {
635
        return [
636
            ['test t1', '[[test]] [[t1]]'],
637
            ['test as t1', '[[test]] [[t1]]'],
638
            ['test AS t1', '[[test]] [[t1]]'],
639
            ['test', '[[test]]'],
640
        ];
641
    }
642
643
    public function buildLikeConditionsProvider(): array
644
    {
645
        $conditions = [
646
            /* simple like */
647
            [['like', 'name', 'foo%'], '[[name]] LIKE :qp0', [':qp0' => '%foo\%%']],
648
            [['not like', 'name', 'foo%'], '[[name]] NOT LIKE :qp0', [':qp0' => '%foo\%%']],
649
            [['or like', 'name', 'foo%'], '[[name]] LIKE :qp0', [':qp0' => '%foo\%%']],
650
            [['or not like', 'name', 'foo%'], '[[name]] NOT LIKE :qp0', [':qp0' => '%foo\%%']],
651
652
            /* like for many values */
653
            [
654
                ['like', 'name', ['foo%', '[abc]']],
655
                '[[name]] LIKE :qp0 AND [[name]] LIKE :qp1',
656
                [':qp0' => '%foo\%%', ':qp1' => '%[abc]%'],
657
            ],
658
            [
659
                ['not like', 'name', ['foo%', '[abc]']],
660
                '[[name]] NOT LIKE :qp0 AND [[name]] NOT LIKE :qp1',
661
                [':qp0' => '%foo\%%', ':qp1' => '%[abc]%'],
662
            ],
663
            [
664
                ['or like', 'name', ['foo%', '[abc]']],
665
                '[[name]] LIKE :qp0 OR [[name]] LIKE :qp1',
666
                [':qp0' => '%foo\%%', ':qp1' => '%[abc]%'],
667
            ],
668
            [
669
                ['or not like', 'name', ['foo%', '[abc]']],
670
                '[[name]] NOT LIKE :qp0 OR [[name]] NOT LIKE :qp1',
671
                [':qp0' => '%foo\%%', ':qp1' => '%[abc]%'],
672
            ],
673
674
            /* like with Expression */
675
            [
676
                ['like', 'name', new Expression('CONCAT("test", name, "%")')],
677
                '[[name]] LIKE CONCAT("test", name, "%")',
678
                [],
679
            ],
680
            [
681
                ['not like', 'name', new Expression('CONCAT("test", name, "%")')],
682
                '[[name]] NOT LIKE CONCAT("test", name, "%")',
683
                [],
684
            ],
685
            [
686
                ['or like', 'name', new Expression('CONCAT("test", name, "%")')],
687
                '[[name]] LIKE CONCAT("test", name, "%")',
688
                [],
689
            ],
690
            [
691
                ['or not like', 'name', new Expression('CONCAT("test", name, "%")')],
692
                '[[name]] NOT LIKE CONCAT("test", name, "%")',
693
                [],
694
            ],
695
            [
696
                ['like', 'name', [new Expression('CONCAT("test", name, "%")'), '\ab_c']],
697
                '[[name]] LIKE CONCAT("test", name, "%") AND [[name]] LIKE :qp0',
698
                [':qp0' => '%\\\ab\_c%'],
699
            ],
700
            [
701
                ['not like', 'name', [new Expression('CONCAT("test", name, "%")'), '\ab_c']],
702
                '[[name]] NOT LIKE CONCAT("test", name, "%") AND [[name]] NOT LIKE :qp0',
703
                [':qp0' => '%\\\ab\_c%'],
704
            ],
705
            [
706
                ['or like', 'name', [new Expression('CONCAT("test", name, "%")'), '\ab_c']],
707
                '[[name]] LIKE CONCAT("test", name, "%") OR [[name]] LIKE :qp0',
708
                [':qp0' => '%\\\ab\_c%'],
709
            ],
710
            [
711
                ['or not like', 'name', [new Expression('CONCAT("test", name, "%")'), '\ab_c']],
712
                '[[name]] NOT LIKE CONCAT("test", name, "%") OR [[name]] NOT LIKE :qp0',
713
                [':qp0' => '%\\\ab\_c%'],
714
            ],
715
716
            /**
717
             * {@see https://github.com/yiisoft/yii2/issues/15630}
718
             */
719
            [
720
                ['like', 'location.title_ru', 'vi%', null],
721
                '[[location]].[[title_ru]] LIKE :qp0',
722
                [':qp0' => 'vi%'],
723
            ],
724
725
            /* like object conditions */
726
            [
727
                new LikeCondition('name', 'like', new Expression('CONCAT("test", name, "%")')),
728
                '[[name]] LIKE CONCAT("test", name, "%")',
729
                [],
730
            ],
731
            [
732
                new LikeCondition('name', 'not like', new Expression('CONCAT("test", name, "%")')),
733
                '[[name]] NOT LIKE CONCAT("test", name, "%")',
734
                [],
735
            ],
736
            [
737
                new LikeCondition('name', 'or like', new Expression('CONCAT("test", name, "%")')),
738
                '[[name]] LIKE CONCAT("test", name, "%")',
739
                [],
740
            ],
741
            [
742
                new LikeCondition('name', 'or not like', new Expression('CONCAT("test", name, "%")')),
743
                '[[name]] NOT LIKE CONCAT("test", name, "%")',
744
                [],
745
            ],
746
            [
747
                new LikeCondition('name', 'like', [new Expression('CONCAT("test", name, "%")'), '\ab_c']),
748
                '[[name]] LIKE CONCAT("test", name, "%") AND [[name]] LIKE :qp0',
749
                [':qp0' => '%\\\ab\_c%'],
750
            ],
751
            [
752
                new LikeCondition('name', 'not like', [new Expression('CONCAT("test", name, "%")'), '\ab_c']),
753
                '[[name]] NOT LIKE CONCAT("test", name, "%") AND [[name]] NOT LIKE :qp0',
754
                [':qp0' => '%\\\ab\_c%'],
755
            ],
756
            [
757
                new LikeCondition('name', 'or like', [new Expression('CONCAT("test", name, "%")'), '\ab_c']),
758
                '[[name]] LIKE CONCAT("test", name, "%") OR [[name]] LIKE :qp0', [':qp0' => '%\\\ab\_c%'],
759
            ],
760
            [
761
                new LikeCondition('name', 'or not like', [new Expression('CONCAT("test", name, "%")'), '\ab_c']),
762
                '[[name]] NOT LIKE CONCAT("test", name, "%") OR [[name]] NOT LIKE :qp0', [':qp0' => '%\\\ab\_c%'],
763
            ],
764
765
            /* like with expression as columnName */
766
            [['like', new Expression('name'), 'teststring'], 'name LIKE :qp0', [':qp0' => '%teststring%']],
767
        ];
768
769
        /* adjust dbms specific escaping */
770
        foreach ($conditions as $i => $condition) {
771
            $conditions[$i][1] = DbHelper::replaceQuotes($condition[1], $this->db->getDriverName());
772
            if (!empty($this->likeEscapeCharSql)) {
773
                preg_match_all('/(?P<condition>LIKE.+?)( AND| OR|$)/', $conditions[$i][1], $matches, PREG_SET_ORDER);
774
775
                foreach ($matches as $match) {
776
                    $conditions[$i][1] = str_replace(
777
                        $match['condition'],
778
                        $match['condition'] . $this->likeEscapeCharSql,
779
                        $conditions[$i][1]
780
                    );
781
                }
782
            }
783
784
            foreach ($conditions[$i][2] as $name => $value) {
785
                $conditions[$i][2][$name] = strtr($conditions[$i][2][$name], $this->likeParameterReplacements);
786
            }
787
        }
788
789
        return $conditions;
790
    }
791
792
    public function createDropIndexesProvider(): array
793
    {
794
        $tableName = 'T_constraints_2';
795
        $name1 = 'CN_constraints_2_single';
796
        $name2 = 'CN_constraints_2_multi';
797
798
        return [
799
            'drop' => [
800
                "DROP INDEX [[$name1]] ON {{{$tableName}}}",
801
                static function (QueryBuilderInterface $qb) use ($tableName, $name1) {
802
                    return $qb->dropIndex($name1, $tableName);
803
                },
804
            ],
805
            'create' => [
806
                "CREATE INDEX [[$name1]] ON {{{$tableName}}} ([[C_index_1]])",
807
                static function (QueryBuilderInterface $qb) use ($tableName, $name1) {
808
                    return $qb->createIndex($name1, $tableName, 'C_index_1');
809
                },
810
            ],
811
            'create (2 columns)' => [
812
                "CREATE INDEX [[$name2]] ON {{{$tableName}}} ([[C_index_2_1]], [[C_index_2_2]])",
813
                static function (QueryBuilderInterface $qb) use ($tableName, $name2) {
814
                    return $qb->createIndex($name2, $tableName, 'C_index_2_1, C_index_2_2');
815
                },
816
            ],
817
            'create unique' => [
818
                "CREATE UNIQUE INDEX [[$name1]] ON {{{$tableName}}} ([[C_index_1]])",
819
                static function (QueryBuilderInterface $qb) use ($tableName, $name1) {
820
                    return $qb->createIndex($name1, $tableName, 'C_index_1', QueryBuilder::INDEX_UNIQUE);
821
                },
822
            ],
823
            'create unique (2 columns)' => [
824
                "CREATE UNIQUE INDEX [[$name2]] ON {{{$tableName}}} ([[C_index_2_1]], [[C_index_2_2]])",
825
                static function (QueryBuilderInterface $qb) use ($tableName, $name2) {
826
                    return $qb->createIndex($name2, $tableName, 'C_index_2_1, C_index_2_2', QueryBuilder::INDEX_UNIQUE);
827
                },
828
            ],
829
        ];
830
    }
831
832
    public function deleteProvider(): array
833
    {
834
        return [
835
            [
836
                'user',
837
                [
838
                    'is_enabled' => false,
839
                    'power' => new Expression('WRONG_POWER()'),
840
                ],
841
                DbHelper::replaceQuotes(
842
                    'DELETE FROM [[user]] WHERE ([[is_enabled]]=:qp0) AND ([[power]]=WRONG_POWER())',
843
                    $this->db->getDriverName(),
844
                ),
845
                [
846
                    ':qp0' => false,
847
                ],
848
            ],
849
        ];
850
    }
851
852
    public function updateProvider(): array
853
    {
854
        return [
855
            [
856
                'customer',
857
                [
858
                    'status' => 1,
859
                    'updated_at' => new Expression('now()'),
860
                ],
861
                [
862
                    'id' => 100,
863
                ],
864
                DbHelper::replaceQuotes(
865
                    'UPDATE [[customer]] SET [[status]]=:qp0, [[updated_at]]=now() WHERE [[id]]=:qp1',
866
                    $this->db->getDriverName(),
867
                ),
868
                [
869
                    ':qp0' => 1,
870
                    ':qp1' => 100,
871
                ],
872
            ],
873
        ];
874
    }
875
876
    public function upsertProvider(): array
877
    {
878
        return [
879
            'regular values' => [
880
                'T_upsert',
881
                [
882
                    'email' => '[email protected]',
883
                    'address' => 'bar {{city}}',
884
                    'status' => 1,
885
                    'profile_id' => null,
886
                ],
887
                true,
888
                null,
889
                [
890
                    ':qp0' => '[email protected]',
891
                    ':qp1' => 'bar {{city}}',
892
                    ':qp2' => 1,
893
                    ':qp3' => null,
894
                ],
895
            ],
896
            'regular values with update part' => [
897
                'T_upsert',
898
                [
899
                    'email' => '[email protected]',
900
                    'address' => 'bar {{city}}',
901
                    'status' => 1,
902
                    'profile_id' => null,
903
                ],
904
                [
905
                    'address' => 'foo {{city}}',
906
                    'status' => 2,
907
                    'orders' => new Expression('T_upsert.orders + 1'),
908
                ],
909
                null,
910
                [
911
                    ':qp0' => '[email protected]',
912
                    ':qp1' => 'bar {{city}}',
913
                    ':qp2' => 1,
914
                    ':qp3' => null,
915
                    ':qp4' => 'foo {{city}}',
916
                    ':qp5' => 2,
917
                ],
918
            ],
919
            'regular values without update part' => [
920
                'T_upsert',
921
                [
922
                    'email' => '[email protected]',
923
                    'address' => 'bar {{city}}',
924
                    'status' => 1,
925
                    'profile_id' => null,
926
                ],
927
                false,
928
                null,
929
                [
930
                    ':qp0' => '[email protected]',
931
                    ':qp1' => 'bar {{city}}',
932
                    ':qp2' => 1,
933
                    ':qp3' => null,
934
                ],
935
            ],
936
            'query' => [
937
                'T_upsert',
938
                (new Query($this->db))
939
                    ->select([
940
                        'email',
941
                        'status' => new Expression('2'),
942
                    ])
943
                    ->from('customer')
944
                    ->where(['name' => 'user1'])
945
                    ->limit(1),
946
                true,
947
                null,
948
                [
949
                    ':qp0' => 'user1',
950
                ],
951
            ],
952
            'query with update part' => [
953
                'T_upsert',
954
                (new Query($this->db))
955
                    ->select([
956
                        'email',
957
                        'status' => new Expression('2'),
958
                    ])
959
                    ->from('customer')
960
                    ->where(['name' => 'user1'])
961
                    ->limit(1),
962
                [
963
                    'address' => 'foo {{city}}',
964
                    'status' => 2,
965
                    'orders' => new Expression('T_upsert.orders + 1'),
966
                ],
967
                null,
968
                [
969
                    ':qp0' => 'user1',
970
                    ':qp1' => 'foo {{city}}',
971
                    ':qp2' => 2,
972
                ],
973
            ],
974
            'query without update part' => [
975
                'T_upsert',
976
                (new Query($this->db))
977
                    ->select([
978
                        'email',
979
                        'status' => new Expression('2'),
980
                    ])
981
                    ->from('customer')
982
                    ->where(['name' => 'user1'])
983
                    ->limit(1),
984
                false,
985
                null,
986
                [
987
                    ':qp0' => 'user1',
988
                ],
989
            ],
990
            'values and expressions' => [
991
                '{{%T_upsert}}',
992
                [
993
                    '{{%T_upsert}}.[[email]]' => '[email protected]',
994
                    '[[ts]]' => new Expression('now()'),
995
                ],
996
                true,
997
                null,
998
                [
999
                    ':qp0' => '[email protected]',
1000
                ],
1001
            ],
1002
            'values and expressions with update part' => [
1003
                '{{%T_upsert}}',
1004
                [
1005
                    '{{%T_upsert}}.[[email]]' => '[email protected]',
1006
                    '[[ts]]' => new Expression('now()'),
1007
                ],
1008
                [
1009
                    '[[orders]]' => new Expression('T_upsert.orders + 1'),
1010
                ],
1011
                null,
1012
                [
1013
                    ':qp0' => '[email protected]',
1014
                ],
1015
            ],
1016
            'values and expressions without update part' => [
1017
                '{{%T_upsert}}',
1018
                [
1019
                    '{{%T_upsert}}.[[email]]' => '[email protected]',
1020
                    '[[ts]]' => new Expression('now()'),
1021
                ],
1022
                false,
1023
                null,
1024
                [
1025
                    ':qp0' => '[email protected]',
1026
                ],
1027
            ],
1028
            'query, values and expressions with update part' => [
1029
                '{{%T_upsert}}',
1030
                (new Query($this->db))
1031
                    ->select([
1032
                        'email' => new Expression(':phEmail', [':phEmail' => '[email protected]']),
1033
                        '[[time]]' => new Expression('now()'),
1034
                    ]),
1035
                [
1036
                    'ts' => 0,
1037
                    '[[orders]]' => new Expression('T_upsert.orders + 1'),
1038
                ],
1039
                null,
1040
                [
1041
                    ':phEmail' => '[email protected]',
1042
                    ':qp1' => 0,
1043
                ],
1044
            ],
1045
            'query, values and expressions without update part' => [
1046
                '{{%T_upsert}}',
1047
                (new Query($this->db))
1048
                    ->select([
1049
                        'email' => new Expression(':phEmail', [':phEmail' => '[email protected]']),
1050
                        '[[time]]' => new Expression('now()'),
1051
                    ]),
1052
                [
1053
                    'ts' => 0,
1054
                    '[[orders]]' => new Expression('T_upsert.orders + 1'),
1055
                ],
1056
                null,
1057
                [
1058
                    ':phEmail' => '[email protected]',
1059
                    ':qp1' => 0,
1060
                ],
1061
            ],
1062
            'no columns to update' => [
1063
                'T_upsert_1',
1064
                [
1065
                    'a' => 1,
1066
                ],
1067
                false,
1068
                null,
1069
                [
1070
                    ':qp0' => 1,
1071
                ],
1072
            ],
1073
        ];
1074
    }
1075
}
1076