Passed
Branch dev (f56f10)
by Wilmer
04:41 queued 01:34
created

QueryBuilderProvider::deleteProvider()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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