Passed
Pull Request — master (#372)
by Wilmer
16:16
created

AbstractQueryBuilderProvider   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 1016
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 578
c 0
b 0
f 0
dl 0
loc 1016
rs 10
wmc 19

13 Methods

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